coldblade2000 coldblade2000 - 18 days ago 9
Android Question

How to change colors of parent Fragment and views of RecyclerView depending on OnClickListener position from recyclerview

This is a difficult one, so I attached a picture to help visualize a bit.
enter image description here

So I have a recyclerview which contains a palette of colors. It loads the colors by creating a new ImageView for each color, and then changing the color and drawable of each imageview, having a different one for the "selected color". The selected color is also used to change the color of the toolbar from the parent DialogFragment.

The problem comes that the OnClicklistener hasn't worked at all after hours of research. Right now, I can't use an OnClickListener without the app crashing.

Now, lets get to the actual code and problems.
I'll declare (to you) some variables and classes I'm using:

ArrayList<String> colorsList = //arraylist which contains all the colors which can be displayed
NewNotebookFragment = //DialogFragment, contains a toolbar, miscellaneous views and a rvNewNotebook
RecyclerView rvNewNotebook = //the recyclerview containing imageviews representing each individual color retrieved from colorsList
NewNotebookAdapter = adapter which is bound to rvNewNotebook


Right now, what I've done is make a method in NewNotebookFragment called changeColor which can receive an int, representing the position of the color which was selected. changeColor() changes the color of the toolbar. changeColor() is called from an onClick method from an OnclickListener, defined in the Viewholder in NewNotebookAdapter.
Now comes the actual code.

newNotebookFragment:

public class NewNotebookFragment extends DialogFragment {

RecyclerView rvNewNotebook;
int activeColor;
ArrayList<String> colors=Helpers.getPossibleColors();
@Override
public View onCreateView ...}
@Override
public void onViewCreated(View v, Bundle savedInstanceState){

activeColor=new Random().nextInt(colors.size());

toolbar = (Toolbar) v.findViewById(R.id.newnotebooktoolbar);
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
return false;
}
});
toolbar.inflateMenu(R.menu.newnotebook);
toolbar.setTitle("Create new Notebook");

if(Helpers.isColorDark(Color.parseColor(colors.get(activeColor)))){
toolbar.setTitleTextColor(getResources().getColor(R.color.md_dark_primary_text));
}else{
toolbar.setTitleTextColor(getResources().getColor(R.color.md_light_primary_text));
}
rvNewNotebook = (RecyclerView) v.findViewById(R.id.rvNewNotebook);
final GridLayoutManager rvNotebookManager = new GridLayoutManager(getContext(),6);
rvNewNotebook.setLayoutManager(rvNotebookManager);
NewNotebookAdapter adapter= new NewNotebookAdapter(getContext(), Helpers.getPossibleColors(), activeColor);

toolbar.setBackgroundColor(Color.parseColor(Helpers.getPossibleColors().get(activeColor)));
rvNewNotebook.setAdapter(adapter);
Log.d("onViewCreated", ""+rvNewNotebook.getWidth());


}
@Override public void onResume(){...}
public static NewNotebookFragment newInstance() {...}

public void changeColor(int position){
if(Helpers.isColorDark(Color.parseColor(colors.get(position)))){
toolbar.setTitleTextColor(getResources().getColor(R.color.md_dark_primary_text));
}else{
toolbar.setTitleTextColor(getResources().getColor(R.color.md_light_primary_text));
}
toolbar.setBackgroundColor(Color.parseColor(Helpers.getPossibleColors().get(position)));
}


NewNotebookAdapter:

public class NewNotebookAdapter extends RecyclerView.Adapter<NewNotebookAdapter.ViewHolder>{
Context context;
ArrayList<String> colors = new ArrayList<>();
int activeColor;
public NewNotebookAdapter(){

}

public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public ImageView ivSwatch;
public Toolbar toolbar;
public ViewHolder(View itemView) {
super(itemView);
ivSwatch = (ImageView) itemView.findViewById(R.id.ivSwatch);
toolbar = (Toolbar) itemView.findViewById(R.id.newnotebooktoolbar);
ivSwatch.setOnClickListener(this);
}


@Override
public void onClick(View view) {
NewNotebookFragment newNotebookFragment = new NewNotebookFragment();
final int adapterPosition = ViewHolder.this.getAdapterPosition();
Log.d("OnClick. Adapter", "getAdapterPosition: "+adapterPosition);
newNotebookFragment.changeColor(adapterPosition);
}
}
public NewNotebookAdapter(Context context, ArrayList<String> colors, int activeColor){
this.context=context;
this.colors=colors;
this.activeColor=activeColor;
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.colorcircle,parent,false);
return new ViewHolder(v);
}


@Override
public void onBindViewHolder(ViewHolder holder, int position) {

Drawable drawablefull = ContextCompat.getDrawable(context,R.drawable.colorcircle);
Drawable drawableHollow = ContextCompat.getDrawable(context,R.drawable.coloroutlinecircle);
if (position==activeColor){
holder.ivSwatch.setImageDrawable(drawablefull.mutate());
}else if (position!=activeColor){
holder.ivSwatch.setImageDrawable(drawableHollow.mutate());
}
holder.ivSwatch.setColorFilter(Color.parseColor(colors.get(position)));


The current problem I have is the following stack trace:

FATAL EXCEPTION: main
Process: com.twotowerstudios.virtualnotebookdesign, PID: 32272
java.lang.IllegalStateException: Fragment NewNotebookFragment{b10003e} not attached to Activity
at android.support.v4.app.Fragment.getResources(Fragment.java:648)
at com.twotowerstudios.virtualnotebookdesign.NewNotebookDialog.NewNotebookFragment.changeColor(NewNotebookFragment.java:85)
at com.twotowerstudios.virtualnotebookdesign.NewNotebookDialog.NewNotebookAdapter$ViewHolder.onClick(NewNotebookAdapter.java:49)
at android.view.View.performClick(View.java)
at android.view.View$PerformClick.run(View.java)
at android.os.Handler.handleCallback(Handler.java)
at android.os.Handler.dispatchMessage(Handler.java)
at android.os.Looper.loop(Looper.java)
at android.app.ActivityThread.main(ActivityThread.java)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java)
at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)


Now I'm almost positive this is related to my onClick method, which creates a new instance of NewNotebookFragment, causing the fragment I'm using in the onClick method to not actually be the same as the one which is already being used and displayed. HOWEVER, I'm making this thread because I have no real idea on how to solve the issue as a whole. I don't know how I could for example get the actual NewNotebookFragment from the onClick method so I could modify it or call methods from it. I don't know if once that
s fixed if it would even work or anything. How could I get this working?

Edit: I got it working perfectly. Basically what I did was define the setonclickListener inside the onBindViewHolder, on the Imageviews. The clickListener will call an interface called AdapterInterface, and use a method in it called clickListener, which allows me to pass the int of the color to the parent fragment. The fragment will implement that interface defined in the adapter, and it will override clickListener, using the color passed to it and from it invoking the changeColor method. If anyone in the future needs examples, feel free to contact me. The relevant changes are in these two files, uploaded to Github: https://gist.github.com/coldblade2000/0c2cac8b1af4df5985fe6cebebc9cff2 The links contain a certain level of explanation and documentation about the two classes

Answer

Looking at your code, you're creating a new Fragment every time it's clicked. I believe what you want is to change color of the dialog fragment toolbar. The first thing you can do is to create an interface listener for as a Callback to Fragment, which is passed into Adapter. Something like this

  public interface ViewHolderOnClickListener {

    //This method can be any parameters, I'm pasing color here since you need the color code
    public void onViewHolderClick(View itemView, int position, int color);
  }

And in your adapter, and viewholder Constructor methods, add an interface instance as your parameter. You can either directly pass it on creating Adapter or make your Fragment implements the interface and pass it with "this"

  public TempAdapter(ViewHolderOnClickListener viewHolderOnClickListener) {
    this.viewHolderOnClickListener = viewHolderOnClickListener;
  }

And In your ViewHolder, do a callback with

@Override
public void onClick(View view) {
  viewHolderOnClickListener.onViewHolderClick(*PARAMS HERE*);
}

That way, your fragment can listen to OnClick That is happening inside your ViewHolder, and respond accordingly.

Another way is to use EventBuses

Comments