I am running into a strange issue with the RecyclerView loading images dynamically with Glide. Looks like race condition and I looked at this thread but it did not solve the problem:
Recyclerview Adapter and Glide - same image every 4-5 rows
Here's how it looks like screenshot and the red arrows shows the image glitch, notice that the user name text ALWAYS exist.
The code itself looks pretty straight forward:
RV Adapter:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
int viewType = getItemViewType(position);
bindSubscribersCell((SubscribersListViewHolder) viewHolder, position);
}
binding view:
private void bindSubscribersCell(SubscribersListViewHolder holder, int position) {
SubscribersListRowTypeUser subscriberRow = (SubscribersListRowTypeUser) mItems.get(position);
String userFullName = SDKUserHelper.MakeFullName(subscriberRow.getUser());
holder.getUsername().setText(userFullName);
holder.getUsername().setVisibility(View.VISIBLE);
CircleImageView userImageView = holder.getAvatarImage();
Drawable defaultIcon = ContextCompat.getDrawable(
userImageView.getContext(),
R.drawable.avatar_guest);
RecylerViewHelper.loadImageView(
subscriberRow.getUser().getPhotoUrl(),
userImageView,
defaultIcon,
true);
}
the image load helper using the same technique described on the thread I mentioned above:
public static void loadImageView(
String imageUrl,
ImageView imageView,
Drawable drawable,
boolean icon) {
Context context = imageView.getContext();
if (imageUrl != null && !imageUrl.isEmpty()) {
if (icon) {
Glide.with(context).using(CloudinaryHelper.getUrlLoaderPresetPOIIcon(context)).
load(imageUrl).
centerCrop().
crossFade().
into(imageView);
} else {
Glide.with(context).
load(imageUrl).
crossFade().
into(imageView);
}
} else {
Glide.clear(imageView);
imageView.setImageDrawable(drawable);
}
}
the inner layout of the cell:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical">
<de.hdodenhof.circleimageview.CircleImageView
android:id="#+id/user_avatar"
android:layout_width="wrap_content"
android:layout_height="#dimen/splash_icon_height"
android:layout_centerVertical="true"
android:src="#drawable/avatar_guest" />
<TextView
android:id="#+id/user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginEnd="40dp"
android:layout_toEndOf="#+id/user_avatar"
android:ellipsize="end"
android:gravity="center"
android:visibility="gone"
android:maxLines="1"
android:text=""
android:textSize="14sp" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="20dp"
android:src="#drawable/arrow_forward_gray" />
</RelativeLayout>
the wrapper class to fetch the layout controls:
public class SubscribersListViewHolder extends RecyclerView.ViewHolder {
final private CircleImageView mAvatarImage;
final private TextView mUsername;
final private View mViewHolder;
public SubscribersListViewHolder(View itemView) {
super(itemView);
mViewHolder = itemView;
mUsername = (TextView) itemView.findViewById(R.id.user_name);
assert mUsername != null;
mAvatarImage = (CircleImageView) itemView.findViewById(R.id.user_avatar);
assert mAvatarImage != null;
}
public TextView getUsername() { return mUsername; }
public CircleImageView getAvatarImage() { return mAvatarImage; }
public View getViewHolder() { return mViewHolder; }
}
If anyone can spot the race condition, I really appreciate.
thank you!
The fix was to set the icon width and height to a fixed value, so instead of "wrap_content" make some fixed dp (in my case 49dp). In other words:
WRONG (race condition)
android:layout_width="wrap_content"
android:layout_height="#dimen/splash_icon_height"
CORRECT:
android:layout_width="#dimen/splash_icon_height"
android:layout_height="#dimen/splash_icon_height"
But the question now becomes why? I have no idea.
Related
In Xamarin Forms we can set the TextCell property like this right?
<ListView x:Name="WorkersListView" BackgroundColor="#ECEFF1" Margin="2" ItemSelected="WorkersListView_ItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell TextColor="Black" Text="{Binding WorkerName}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
By writing ListView.ItemTemplate and DataTemplate we can access the TextCell
But how can i do it on Xamarin Android?
because it doesn't have ListView.ItemTemplate and DataTemplate elements.
This is my current ListView in my Xamarin.Android project
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/WorkersListView"
android:background="#android:color/darker_gray"
android:layout_margin="2dp">
</ListView>
Thanks in advance.
Working with ListView in Xamarin Forms and Xamarin Android is different
Add an XML that looks something like this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fastScrollEnabled="true" />
</LinearLayout>
Then create ListView template
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:minWidth="25px"
android:minHeight="25px"
android:paddingBottom="4dp">
<ImageView
android:id="#+id/photoImageView"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:padding="5dp" />
<TextView
android:id="#+id/nameTextView"
android:layout_toRightOf="#id/photoImageView"
android:text="Name"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="5dp" />
<TextView
android:id="#+id/departmentTextView"
android:layout_toRightOf="#id/photoImageView"
android:layout_below="#id/nameTextView"
android:text="Department"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="5dp" />
</RelativeLayout>
Create an adapter class something like:
public class MyCustomListAdapter : BaseAdapter<User>
{
List<User> users;
public MyCustomListAdapter(List<User> users)
{
this.users = users;
}
public override User this[int position]
{
get
{
return users[position];
}
}
public override int Count
{
get
{
return users.Count;
}
}
public override long GetItemId(int position)
{
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var view = convertView;
if(view==null)
{
view = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.userRow, parent, false);
var photo = view.FindViewById<ImageView>(Resource.Id.photoImageView);
var name = view.FindViewById<TextView>(Resource.Id.nameTextView);
var department = view.FindViewById<TextView>(Resource.Id.departmentTextView);
view.Tag = new ViewHolder() { Photo = photo, Name = name, Department = department };
}
var holder = (ViewHolder)view.Tag;
holder.Photo.SetImageDrawable(ImageManager.Get(parent.Context, users[position].ImageUrl));
holder.Name.Text = users[position].Name;
holder.Department.Text = users[position].Department;
return view;
}
}
A detailed answer is in the blog:
https://www.c-sharpcorner.com/article/creating-custom-listview-in-xamarin-android-app/
ListView in Android instance requires an Adapter to consume the data contained in row Views.
Add one ListView and one TextView control, to display the list of items and the elements respectively. Use the following code.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#6BC6ED"
>
<TextView
android:id="#+id/Name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="25dp"
android:textColor="#FFFFFF"
/>
<ListView
android:id="#+id/demolist"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>
</LinearLayout>
Similar to what we did in Forms , create new model class which contains the binding property in ViewCell.
For example
public class Students {
public string Name {
get;
set;
}
public int Age {
get;
set;
}
}
Next, you need to create Adapter to bind the data to the ListView. Android has in-built base adapter class for Adapter implementations to bind a ListView to a data source. Also, we can get item position, count, and selected item, etc. there. Count method is used to get the number of rows in the list.
GetView method is used to return a View for each row, populated with which element.
GetItemId method is used to return row data properties like row number and value.
public class StudentAdapter: BaseAdapter < Students > {
public List < Students > sList;
private Context sContext;
public StudentAdapter(Context context, List < Students > list) {
sList = list;
sContext = context;
}
public override Students this[int position] {
get {
return sList[position];
}
}
public override int Count {
get {
return sList.Count;
}
}
public override long GetItemId(int position) {
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent) {
View row = convertView;
try {
if (row == null) {
row = LayoutInflater.From(sContext).Inflate(Resource.Layout.Main, null, false);
}
TextView txtName = row.FindViewById < TextView > (Resource.Id.Name);
txtName.Text = sList[position].Name;
} catch (Exception ex) {
System.Diagnostics.Debug.WriteLine(ex.Message);
} finally {}
return row;
}
}
Next, open your activity file and write the below code to create a list of items to display a ListView and show the selected items from the list.
private ListView studentlistView;
private List < Students > mlist;
StudentAdapter adapter;
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
List < Students > objstud = new List < Students > ();
objstud.Add(new Students {
Name = "Jack", Age = 26
});
objstud.Add(new Students {
Name = "Mary", Age = 26
});
studentlistView = FindViewById < ListView > (Resource.Id.demolist);
mlist = new List < Students > ();
mlist = objstud;
adapter = new StudentAdapter(this, mlist);
studentlistView.Adapter = adapter;
studentlistView.ItemClick += StudentlistView_ItemClick;
}
To handle the selected items, write the below code.
private void StudentlistView_ItemClick(object sender, AdapterView.ItemClickEventArgs e) {
var select = mlist[e.Position].Name;
Android.Widget.Toast.MakeText(this, select, Android.Widget.ToastLength.Long).Show();
}
For more details you can check here .
In my application, I have a ListView to contain the 5 quiz questions and their answers from a json file in server. I want the choices to be Radio Buttons but when my application runs on the emulator, I can see the questions and answers but after clicking one of the radio buttons, some other questions' answers Radio Buttons are checked. How can I prevent that?
Here is my adapter class:
public class FormSoruAdapter extends ArrayAdapter<FormSoru>{
List<FormSoru> formSoruList;
Context context;
private LayoutInflater mInflater;
//Constructor method
public FormSoruAdapter(Context context, List<FormSoru> objects){
super(context, 0, objects);
this.context = context;
this.mInflater = LayoutInflater.from(context);
formSoruList = objects;
}
#Override
public FormSoru getItem(int position){
return formSoruList.get(position);
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
final ViewHolder vh;
if (convertView == null){
View view = mInflater.inflate(R.layout.list_soru,parent,false);
vh = ViewHolder.create((LinearLayout) view);
view.setTag(vh);
} else {
vh = (ViewHolder) convertView.getTag();
}
FormSoru item = getItem(position);
vh.textViewSoruNo.setText(item.getId());
vh.textViewSoru.setText(item.getSoru());
String cevap1 = item.getCevap1();
String cevap2 = item.getCevap2();
String cevap3 = item.getCevap3();
String cevap4 = item.getCevap4();
String cevap5 = item.getCevap5();
if (cevap1 == "" && cevap2 == "" && cevap3 == "" && cevap4 == "" && cevap5 == ""){
vh.radioButtonCevap1.setVisibility(View.GONE);
vh.radioButtonCevap2.setVisibility(View.GONE);
vh.radioButtonCevap3.setVisibility(View.GONE);
vh.radioButtonCevap4.setVisibility(View.GONE);
vh.radioButtonCevap5.setVisibility(View.GONE);
} else {
vh.radioButtonCevap1.setText(item.getCevap1());
vh.radioButtonCevap2.setText(item.getCevap2());
vh.radioButtonCevap3.setText(item.getCevap3());
vh.radioButtonCevap4.setText(item.getCevap4());
vh.radioButtonCevap5.setText(item.getCevap5());
}
return vh.rootView;
}
private static class ViewHolder {
public final LinearLayout rootView;
public final TextView textViewSoruNo;
public final TextView textViewSoru;
public final RadioButton radioButtonCevap1;
public final RadioButton radioButtonCevap2;
public final RadioButton radioButtonCevap3;
public final RadioButton radioButtonCevap4;
public final RadioButton radioButtonCevap5;
private ViewHolder(LinearLayout rootView, TextView textViewSoruNo, TextView textViewSoru, RadioButton radioButtonCevap1, RadioButton radioButtonCevap2, RadioButton radioButtonCevap3, RadioButton radioButtonCevap4, RadioButton radioButtonCevap5){
this.rootView = rootView;
this.textViewSoruNo = textViewSoruNo;
this.textViewSoru = textViewSoru;
this.radioButtonCevap1 = radioButtonCevap1;
this.radioButtonCevap2 = radioButtonCevap2;
this.radioButtonCevap3 = radioButtonCevap3;
this.radioButtonCevap4 = radioButtonCevap4;
this.radioButtonCevap5 = radioButtonCevap5;
}
public static ViewHolder create(LinearLayout rootView){
TextView textViewSoruNo = (TextView) rootView.findViewById(R.id.textViewSoruNo);
TextView textViewSoru = (TextView) rootView.findViewById(R.id.textViewSoru);
RadioButton radioButtonCevap1 = (RadioButton) rootView.findViewById(R.id.radioCevap1);
RadioButton radioButtonCevap2 = (RadioButton) rootView.findViewById(R.id.radioCevap2);
RadioButton radioButtonCevap3 = (RadioButton) rootView.findViewById(R.id.radioCevap3);
RadioButton radioButtonCevap4 = (RadioButton) rootView.findViewById(R.id.radioCevap4);
RadioButton radioButtonCevap5 = (RadioButton) rootView.findViewById(R.id.radioCevap5);
return new ViewHolder(rootView, textViewSoruNo, textViewSoru, radioButtonCevap1, radioButtonCevap2,
radioButtonCevap3, radioButtonCevap4,radioButtonCevap5);
}
}
}
And my fragment is:
public class FormFragment extends Fragment {
private ListView listView;
private View parentView;
private ArrayList<FormSoru> formSoruList;
private FormSoruAdapter adapter;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_anket_soru, container, false);
}
#Override
public void onViewCreated(final View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
formSoruList = new ArrayList<>();
parentView = view.findViewById(R.id.parentLayout);
listView = (ListView) view.findViewById(R.id.listViewSorular);
APIService apıService = RetroClient.getApiService();
Call<FormSorular> call = apıService.getFormSorular();
call.enqueue(new Callback<FormSorular>() {
#Override
public void onResponse(Call<FormSorular> call, Response<FormSorular> response) {
if (response.isSuccessful()){
formSoruList = response.body().getFormSorular();
adapter = new FormSoruAdapter(getActivity(),formSoruList);
listView.setAdapter(adapter);
}
}
#Override
public void onFailure(Call<FormSorular> call, Throwable t) {
}
});
}
}
My fragment's layout is:
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/parentLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Dear User"
android:layout_margin="10dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Welcome"
android:layout_margin="8dp"/>
<ListView
android:id="#+id/listViewSorular"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Görüşleriniz"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Gönder"/>
</LinearLayout>
And my list_soru layout which contains questions is:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textViewSoruNo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Soru No"
android:layout_margin="10dp"/>
<TextView
android:id="#+id/textViewSoru"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Soru"
android:layout_margin="8dp"/>
<RadioGroup
android:id="#+id/radioGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioButton
android:id="#+id/radioCevap1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="cevap1"/>
<RadioButton
android:id="#+id/radioCevap2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="cevap2"/>
<RadioButton
android:id="#+id/radioCevap3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="cevap3"/>
<RadioButton
android:id="#+id/radioCevap4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="cevap4"/>
<RadioButton
android:id="#+id/radioCevap5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="cevap5"/>
</RadioGroup>
Actually I have written a code to select image from gallery,but I dont know how to draw or write a text by hand on it.and Edited image should be saved separately in sdcard.Basically I have to make basic paint app where canvas will be my selected image,and my touch is input to draw a circle or anything.
package listdisplay.example.com.photoedit;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Display;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import java.io.FileDescriptor;
import java.io.IOException;
public class MainActivity_photoedit extends AppCompatActivity {
private static final int RORC =0;
ImageView iview;
Button button,button2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_photoedit);
iview= (ImageView) findViewById(R.id.imageView);
button =(Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent,RORC);
}
}
);
}
#Override
public void onActivityResult(int requestCode,int resultCode,Intent resultData){
if(requestCode==RORC && resultCode==RESULT_OK){
Uri uri =null;
if(resultData!=null){
uri=resultData.getData();
try {
Bitmap bitmap= getBitmapFromUri(uri);
iview.setImageBitmap(bitmap);
}
catch (IOException e){
e.printStackTrace();
}
}
}
}
private Bitmap getBitmapFromUri(Uri uri) throws IOException{
ParcelFileDescriptor parcelFileDescriptor = getContentResolver().openFileDescriptor(uri,"r");
FileDescriptor fileDescriptor= parcelFileDescriptor.getFileDescriptor();
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);
parcelFileDescriptor.close();
return bitmap;
}
}
Also, there is another way to do like this is using FingerPaint Api.
For that download code from here,
"https://github.com/nikt/Finger-Paint"
Yes,It also not using Image-view, But it will pick image from gallery and set it into Draw-view and paint on that image.You can also try this.
I have find solution for your requirement . So try this,But for that you have to take Drawing-view instead of Image-view to draw or write text on Image which is picked from Gallery.
Hereby, I am posting code.
activity_main.xml :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:paddingBottom="5dp"
android:paddingTop="5dp"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageButton
android:id="#+id/buttonNew"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:contentDescription="#string/start_new"
android:src="#drawable/new_pic" />
<ImageButton
android:id="#+id/buttonBrush"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:contentDescription="#string/brush"
android:src="#drawable/brush" />
<ImageButton
android:id="#+id/buttonErase"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:contentDescription="#string/erase"
android:src="#drawable/eraser" />
<ImageButton
android:id="#+id/buttonSave"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:contentDescription="#string/save"
android:src="#drawable/save" />
<ImageButton
android:id="#+id/buttonPickImage"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:contentDescription="#string/save"
android:src="#drawable/save" />
</LinearLayout>
<com.wordpress.priyankvex.paintapp.DrawingView
android:id="#+id/drawing"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="3dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="3dp"
android:layout_weight="1"
android:background="#color/white" />
<!-- Color pallet -->
<LinearLayout
android:id="#+id/paint_colors"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageButton
android:layout_width="#dimen/large_brush"
android:layout_height="#dimen/large_brush"
android:layout_margin="2dp"
android:background="#color/skin"
android:contentDescription="#string/paint"
android:onClick="paintClicked"
android:src="#drawable/pallet"
android:tag="#color/skin" />
<ImageButton
android:layout_width="#dimen/large_brush"
android:layout_height="#dimen/large_brush"
android:layout_margin="2dp"
android:background="#color/black"
android:contentDescription="#string/paint"
android:onClick="paintClicked"
android:src="#drawable/pallet"
android:tag="#color/black" />
<ImageButton
android:layout_width="#dimen/large_brush"
android:layout_height="#dimen/large_brush"
android:layout_margin="2dp"
android:background="#color/red"
android:contentDescription="#string/paint"
android:onClick="paintClicked"
android:src="#drawable/pallet"
android:tag="#color/red" />
<ImageButton
android:layout_width="#dimen/large_brush"
android:layout_height="#dimen/large_brush"
android:layout_margin="2dp"
android:background="#color/green"
android:contentDescription="#string/paint"
android:onClick="paintClicked"
android:src="#drawable/pallet"
android:tag="#color/green" />
<ImageButton
android:layout_width="#dimen/large_brush"
android:layout_height="#dimen/large_brush"
android:layout_margin="2dp"
android:background="#color/blue"
android:contentDescription="#string/paint"
android:onClick="paintClicked"
android:src="#drawable/pallet"
android:tag="#color/blue" />
<ImageButton
android:layout_width="#dimen/large_brush"
android:layout_height="#dimen/large_brush"
android:layout_margin="2dp"
android:background="#color/yellow"
android:contentDescription="#string/paint"
android:onClick="paintClicked"
android:src="#drawable/pallet"
android:tag="#color/yellow" />
</LinearLayout>
</LinearLayout>
MainActivity.java :
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
import java.util.UUID;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private DrawingView mDrawingView;
private ImageButton currPaint, drawButton, eraseButton, newButton, saveButton, buttonPickImage;
private float smallBrush, mediumBrush, largeBrush;
private final int RESULT_LOAD_IMAGE = 20;
private Uri pickedImage;
private String imagePath = "";
public Bitmap PaintBitmap;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDrawingView = (DrawingView) findViewById(R.id.drawing);
// Getting the initial paint color.
LinearLayout paintLayout = (LinearLayout) findViewById(R.id.paint_colors);
// 0th child is white color, so selecting first child to give black as initial color.
currPaint = (ImageButton) paintLayout.getChildAt(1);
currPaint.setImageDrawable(getResources().getDrawable(R.drawable.pallet_pressed));
drawButton = (ImageButton) findViewById(R.id.buttonBrush);
drawButton.setOnClickListener(this);
eraseButton = (ImageButton) findViewById(R.id.buttonErase);
eraseButton.setOnClickListener(this);
newButton = (ImageButton) findViewById(R.id.buttonNew);
newButton.setOnClickListener(this);
saveButton = (ImageButton) findViewById(R.id.buttonSave);
saveButton.setOnClickListener(this);
buttonPickImage = (ImageButton) findViewById(R.id.buttonPickImage);
buttonPickImage.setOnClickListener(this);
smallBrush = getResources().getInteger(R.integer.small_size);
mediumBrush = getResources().getInteger(R.integer.medium_size);
largeBrush = getResources().getInteger(R.integer.large_size);
// Set the initial brush size
mDrawingView.setBrushSize(mediumBrush);
}
/**
* Method is called when color is clicked from pallet.
*
* #param view ImageButton on which click took place.
*/
public void paintClicked(View view) {
if (view != currPaint) {
// Update the color
ImageButton imageButton = (ImageButton) view;
String colorTag = imageButton.getTag().toString();
mDrawingView.setColor(colorTag);
// Swap the backgrounds for last active and currently active image button.
imageButton.setImageDrawable(getResources().getDrawable(R.drawable.pallet_pressed));
currPaint.setImageDrawable(getResources().getDrawable(R.drawable.pallet));
currPaint = (ImageButton) view;
mDrawingView.setErase(false);
mDrawingView.setBrushSize(mDrawingView.getLastBrushSize());
}
}
#Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.buttonBrush:
// Show brush size chooser dialog
showBrushSizeChooserDialog();
break;
case R.id.buttonErase:
// Show eraser size chooser dialog
showEraserSizeChooserDialog();
break;
case R.id.buttonNew:
// Show new painting alert dialog
showNewPaintingAlertDialog();
break;
case R.id.buttonSave:
// Show save painting confirmation dialog.
showSavePaintingConfirmationDialog();
break;
case R.id.buttonPickImage:
// Show save painting confirmation dialog.
Intent i = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, RESULT_LOAD_IMAGE);
break;
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RESULT_LOAD_IMAGE && resultCode == RESULT_OK) {
pickedImage = data.getData();
String[] filePath = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(pickedImage, filePath, null, null, null);
cursor.moveToFirst();
imagePath = cursor.getString(cursor.getColumnIndex(filePath[0]));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
// set Gallery Image to Drawing-view
if (imagePath != null) {
PaintBitmap = getResizeBitmap(imagePath);
BitmapDrawable ob = new BitmapDrawable(getResources(), PaintBitmap);
mDrawingView.setBackgroundDrawable(ob);
}
}
}
}
public static Bitmap getResizeBitmap(String path) {
BitmapFactory.Options op = new BitmapFactory.Options();
op.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bmap = BitmapFactory.decodeFile(path, op);
int nh = (int) (bmap.getHeight() * (1024.0 / bmap.getWidth()));
Bitmap bitmapThumbnail = Bitmap.createScaledBitmap(bmap, 1024, nh, true);
int bitmapWidthMain = bitmapThumbnail.getWidth();
int bitmapHeightMain = bitmapThumbnail.getHeight();
return bitmapThumbnail;
}
private void showBrushSizeChooserDialog() {
final Dialog brushDialog = new Dialog(this);
brushDialog.setContentView(R.layout.dialog_brush_size);
brushDialog.setTitle("Brush size:");
ImageButton smallBtn = (ImageButton) brushDialog.findViewById(R.id.small_brush);
smallBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mDrawingView.setBrushSize(smallBrush);
mDrawingView.setLastBrushSize(smallBrush);
brushDialog.dismiss();
}
});
ImageButton mediumBtn = (ImageButton) brushDialog.findViewById(R.id.medium_brush);
mediumBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mDrawingView.setBrushSize(mediumBrush);
mDrawingView.setLastBrushSize(mediumBrush);
brushDialog.dismiss();
}
});
ImageButton largeBtn = (ImageButton) brushDialog.findViewById(R.id.large_brush);
largeBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mDrawingView.setBrushSize(largeBrush);
mDrawingView.setLastBrushSize(largeBrush);
brushDialog.dismiss();
}
});
mDrawingView.setErase(false);
brushDialog.show();
}
private void showEraserSizeChooserDialog() {
final Dialog brushDialog = new Dialog(this);
brushDialog.setTitle("Eraser size:");
brushDialog.setContentView(R.layout.dialog_brush_size);
ImageButton smallBtn = (ImageButton) brushDialog.findViewById(R.id.small_brush);
smallBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mDrawingView.setErase(true);
mDrawingView.setBrushSize(smallBrush);
brushDialog.dismiss();
}
});
ImageButton mediumBtn = (ImageButton) brushDialog.findViewById(R.id.medium_brush);
mediumBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mDrawingView.setErase(true);
mDrawingView.setBrushSize(mediumBrush);
brushDialog.dismiss();
}
});
ImageButton largeBtn = (ImageButton) brushDialog.findViewById(R.id.large_brush);
largeBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mDrawingView.setErase(true);
mDrawingView.setBrushSize(largeBrush);
brushDialog.dismiss();
}
});
brushDialog.show();
}
private void showNewPaintingAlertDialog() {
AlertDialog.Builder newDialog = new AlertDialog.Builder(this);
newDialog.setTitle("New drawing");
newDialog.setMessage("Start new drawing (you will lose the current drawing)?");
newDialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mDrawingView.startNew();
dialog.dismiss();
}
});
newDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
newDialog.show();
}
private void showSavePaintingConfirmationDialog() {
AlertDialog.Builder saveDialog = new AlertDialog.Builder(this);
saveDialog.setTitle("Save drawing");
saveDialog.setMessage("Save drawing to device Gallery?");
saveDialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//save drawing
mDrawingView.setDrawingCacheEnabled(true);
String imgSaved = MediaStore.Images.Media.insertImage(
getContentResolver(), mDrawingView.getDrawingCache(),
UUID.randomUUID().toString() + ".png", "drawing");
if (imgSaved != null) {
Toast savedToast = Toast.makeText(getApplicationContext(),
"Drawing saved to Gallery!", Toast.LENGTH_SHORT);
savedToast.show();
} else {
Toast unsavedToast = Toast.makeText(getApplicationContext(),
"Oops! Image could not be saved.", Toast.LENGTH_SHORT);
unsavedToast.show();
}
// Destroy the current cache.
mDrawingView.destroyDrawingCache();
}
});
saveDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
saveDialog.show();
}
}
Manifest.xml :
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Try This code, It include brush for draw text on image with different brush size,eraser and color picker to draw text in different colors on text and yes, It will save image with text in gallery.
Hereby, i am attaching screenshot,
Hope, it will help you. :)
I use typical AppBarLayout-CollapsingToolbarLayout-Toolbar bundle, and it works ok. But when I add app:expanded="false" or appBarLayout.setExpanded(false) before collapsingToolbarLayout.setTitle(), I have behavior shown in video below.
https://youtu.be/OgSJomzYmek
Here is layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:expanded="false"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="#+id/action_bar_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:scaleType="centerCrop"
android:src="#drawable/img_nature"
app:layout_collapseMode="parallax" />
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:elevation="4dp"
app:layout_collapseMode="pin" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<include layout="#layout/content_main" />
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:backgroundTint="#color/colorPrimary"
app:layout_anchorGravity="bottom|right|end" />
</android.support.design.widget.CoordinatorLayout>
It must be bug in support library.
Is there solution to correctly collapse toolbar, or any workaround?
Update 1.
Have found "solution". In short:
Override CollapsingToolbarLayout onLayout
onLayout(changed, ...) {
if (changed) super.onLayout(true, ...);
}
Nothing more to override. Than use this custom CollapsingToolbarLayout instead of from support library one.
Do not understand why, but this is necessary.
In Activity
appBarLayout.post(... here first call of appBarLayout.setCollapsed() ... )
Do not use AppBarLayout until it completely measured and laid out.
Update 2.
Update 1 not works perfectly. Much better solution - works ok, at least for a while:
Another solution, seems works perfect, at least for a while...
public class Toolbar extends android.support.v7.widget.Toolbar {
private Integer offset = null;
public Toolbar(Context context) {
super(context);
}
public Toolbar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public Toolbar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void offsetTopAndBottom(int offset) {
super.offsetTopAndBottom(offset);
if (this.offset == null)
this.offset = offset;
}
#Override
public int getTitleMarginTop() {
return offset == null ? super.getTitleMarginTop() : -getTop() + offset + super.getTitleMarginTop();
}
#Override
public int getTitleMarginBottom() {
return offset == null ? super.getTitleMarginBottom() : getTop() - offset + super.getTitleMarginBottom();
}
}
That's all.
When ABL's setExpanded is called, it follows to CTL's onLayout: getDescendantRect and then mCollapsingTextHelper.setCollapsedBounds.
At this moment getDescendantRect calculates base absolute position for title. And calculated position, I suppose, has error - it's shifted on (CTL height - Window inset - Toolbar height). The code above corrects this error.
I'm using RecyclerView along with StaggeredGridLayoutManager for showing items with slightly different appearance. in items I use AspectRatioImageView by JakeWharton (link) to maintain the imageView sizes before the image actually downloaded and shown by Picasso. RV (RecyclerView) is populated by server data in infinite scroll pattern.
The problem is (as shown in included pictures), when scolling, some gaps appear on top and bottom of RV (in picture and sometimes items moves between the columns! (taking screenshot is hard :D)
Edit 2: A really important thing I forgot, is that I use the same layout for all of the items but regarding to adapter items some part of the layouts change their visibility to Gone or vice versa.
My question: Why RV behave like this? Am I doing something wrong? Is this normal in RV?
The code is large and I don't know which part is useful to put here. If you can narrow down the cause I will put some code here.
EDIT: Code parts related to problem
xml:
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/bg_texture"
android:padding="#dimen/padding_lvlhalf"
android:scrollbarStyle="outsideOverlay"/>
initializing view:
#Bind(R.id.recycler_view) RecyclerView mRecyclerView;
.
.
.
mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(columnCount, direction));
mRecyclerView.setAdapter(
new RecyclerViewAdapter<>(ctx, new ViewFiller<Post, DashboardPostViewHolder>() {
#Override
public void fill(Post post, DashboardPostViewHolder holder) {
holder.fill(post);
}
#Override
public DashboardPostViewHolder getViewHolder(View view) {
return new DashboardPostViewHolder(view, receiverName);
}
#Override
public int getLayoutID() {
return R.layout.post_dashboard;
}
});)
My custom Adapter:
public RecyclerViewAdapter(Context context, ViewFiller filler) {
super();
this.filler = filler;
mLayoutInflater = LayoutInflater.from(context);
items = new ArrayList<>();
}
#Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View v = mLayoutInflater.inflate(filler.getLayoutID(), parent, false);
return filler.getViewHolder(v);
}
#Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
filler.fill(getItem(position), holder);
}
#Override
public int getItemCount() {
return items.size();
}
#Override
public long getItemId(int position) {
return position;
}
public T getItem(int position){
return items.get(position);
}
public void addItem(int position, T item){
items.add(position, item);
notifyItemInserted(position);
}
public void addItem(T item){
items.add(item);
notifyItemInserted(items.size() - 1);
}
public void addItems(List<T> data){
int oldSize = items.size();
items.addAll(data);
notifyItemRangeInserted(oldSize, data.size());
}
public void addItemsToListHead(List<T> data){
items.addAll(0, data);
notifyItemRangeInserted(0, data.size());
}
public void resetData(List<T> data) {
this.items = data;
notifyDataSetChanged();
}
public void removeItem(int position){
items.remove(position);
notifyItemRemoved(position);
}
public void clear(){
this.items.clear();
notifyDataSetChanged();
}
public List<T> getItems(){
return items;
}
ViewFiller interface
public interface ViewFiller<D, VH extends BaseViewHolder>{
void fill(D data, VH holder);
VH getViewHolder(View view);
#LayoutRes int getLayoutID();
}
Edit 2 (Cont.): My items xml and how I fill them..
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="#dimen/margin_lvlhalf"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="#+id/post"
android:background="#color/poinila_background">
<TextView
android:layout_gravity="right"
android:text="#string/test_title"
android:layout_margin="#dimen/margin_lvl1"
style="#style/match_wrap.large_text.centered_right"
android:id="#+id/post_title"
android:visibility="gone"/>
<!--Post Image-->
<com.shaya.poinila.android.presentation.view.AspectRatioImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:contentDescription="#string/post_content_description"
android:id="#+id/post_image"
android:visibility="gone"
app:aspectRatioEnabled="true"
app:dominantMeasurement="width"
/>
<!--Content-->
<TextView
android:id="#+id/post_content"
style="#style/match_wrap.medium_text.three_line.end_ellipsize"
android:layout_margin="#dimen/margin_lvl1"/>
<!--android:text="#string/test_long"-->
<include layout="#layout/horizontal_line"
/>
<!--Post Author-->
<include android:id="#+id/post_author"
layout="#layout/circle_image_title_subtitle_reverse"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="#dimen/margin_lvl1"/>
<include layout="#layout/horizontal_line"
/>
<!--Post Stats (favs, comments, etc) -->
<include android:id="#+id/post_stats"
layout="#layout/stats"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/margin_lvlhalf"
android:layout_marginBottom="#dimen/margin_lvlhalf"/>
<include layout="#layout/horizontal_line"/>
<!--Post Collection Info-->
<include layout="#layout/rounded_image_title_subtitle_reverse"
android:id="#+id/post_collection"/>
</LinearLayout>
</android.support.v7.widget.CardView>
PostViewHolder class
public class PostViewHolder extends RecyclerView.ViewHolder
public void fill(Post post) {
if (post.type == PostType.IMAGE) {
postImage.setVisibility(View.VISIBLE);
postName.setVisibility(TextUtils.isEmpty(post.name) ? View.VISIBLE : View.GONE);
/*if (TextUtils.isEmpty(post.summary))
postContent.setVisibility(View.GONE);*/
}else{ //post.type == PostType.TEXT
postImage.setVisibility(View.GONE);
postName.setVisibility(View.VISIBLE);
postContent.setVisibility(View.VISIBLE);
}
// contetnt
if (TextUtils.isEmpty(post.summary) && !TextUtils.isEmpty(post.contentUrl)) {
DataRepository.getInstance().getPostContent(post.contentUrl, postContent);
}
else{
setText(postContent, post.summary);
}
.
.
.
}
}
Sorry for the long post!
You probably have margins in your root ViewGroup of item.
manager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE);
Do this ,spacing between the grid items stay same
private boolean flagDecoration = false;
if(!flagDecoration)
{
ItemOffsetDecoration itemDecoration = new ItemOffsetDecoration(getContext(),(R.dimen.spacing));
recyclerView.addItemDecoration(itemDecoration);
flagDecoration = true;
}