Reputation: 8408
For some reason, the Item C CardView
is the only one that animates - even when the Item B or the Item A CardView
is clicked. The French vowels CardView
is not applicable as it works fine. Does anyone know how to fix this so that the correct CardView animates?
GridViewCustom class for the GridView
public class GridViewCustom extends GridView {
public GridViewCustom(Context context) {
super(context);
}
public GridViewCustom(Context context, AttributeSet attrs) {
super(context, attrs);
}
public GridViewCustom(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightSpec;
if (getLayoutParams().height == AbsListView.LayoutParams.WRAP_CONTENT) {
heightSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
}
else {
//Any other height should be respected as is.
heightSpec = heightMeasureSpec;
}
super.onMeasure(widthMeasureSpec, heightSpec);
}
}
fragment class
public class MyFragmentRV extends android.support.v4.app.Fragment {
private Boolean mCurrentValue;
public int mGridViewHeight;
public int txtSubtitleHeight;
private static final int ITEM_TYPE = 100;
private static final int HEADER_TYPE = 101;
private static final int HEADER_TYPE_2 = 102;
private static final int GRID_TYPE = 103;
ValueAnimator mAnimatorGV, mAnimatorTV;
TextView txtArrowGV, txtTitle;
static final String[] frenchVowels = new String[]{
"a", "e", "i", "o", "u", "y"
};
public MyFragmentRV.MyAdapter adapterGV;
public MyFragmentRV() {}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_rv, container, false);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
View v = getView();
assert v != null;
recyclerView = v.findViewById(R.id.my_recyclerview);
// set the linear layout manager
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
// SpannableStrings
int[] attrS = {R.attr.spannablestringtextColor};
TypedArray ta = getActivity().getTheme().obtainStyledAttributes(attrS);
int colorSS = ta.getColor(0, Color.BLACK); //Color.BLACK - default value (colour will change automatically depending on chosen theme)
Log.d(TAG, "clickMethod 1) " + Integer.toHexString(colorSS));
ta.recycle();
// SpannableString (start)
SpannableStringBuilder ssb = new SpannableStringBuilder();
SpannableString str1 = new SpannableString(" Item A ");
str1.setSpan(new BackgroundColorSpan(Color.BLACK), 0, str1.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
str1.setSpan(new ForegroundColorSpan(ContextCompat.getColor(getContext(), R.color.yellow)), 0, str1.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append(str1);
SpannableString str2 = new SpannableString(" Hello World");
str2.setSpan(new ForegroundColorSpan(colorSS), 0, str2.length(), 0);
ssb.append(str2);
// SpannableString (end)
// init data
data = new ArrayList<>();
data.add(ssb);
data.add("Item B");
data.add("Item C");
subdata = new ArrayList<>();
subdata.add("\u2022 a");
subdata.add("\u2022 b\n\u2022 bb");
subdata.add("\u2022 c\n\u2022 cc\n\u2022 ccc");
adapter = createAdapter();
recyclerView.setAdapter(adapter);
super.onActivityCreated(savedInstanceState);
}
RecyclerView recyclerView;
ArrayList<CharSequence> data;
ArrayList<String> subdata;
RecyclerView.Adapter<ViewHolder> adapter;
// creates the adapter
private RecyclerView.Adapter<ViewHolder> createAdapter() {
return new RecyclerView.Adapter<ViewHolder>() {
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int type) {
switch (type) {
case HEADER_TYPE:
return new ViewHolder(inflateHelper(R.layout.header, parent));
case HEADER_TYPE_2:
return new ViewHolder(inflateHelper(R.layout.header, parent));
case ITEM_TYPE:
return new ViewHolder(inflateHelper(R.layout.recyclerview_item_tv, parent));
case GRID_TYPE:
return new ViewHolder(inflateHelper(R.layout.recyclerview_item_gv, parent));
default:
return new ViewHolder(inflateHelper(R.layout.recyclerview_item_tv, parent));
}
}
@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
final Typeface iconFont = FontManager.getTypeface(getContext(), FontManager.FONTAWESOME);
switch (getItemViewType(position)) {
case HEADER_TYPE:
Button expandButton = viewHolder.itemView.findViewById(R.id.button);
expandButton.setText("Expand all");
break;
case HEADER_TYPE_2:
Button collapseButton = viewHolder.itemView.findViewById(R.id.button);
collapseButton.setText("Collapse all");
break;
case ITEM_TYPE:
// get the current item
CharSequence itemA = data.get(position - 3);
String itemB = subdata.get(position - 3);
//
txtTitle = viewHolder.itemView.findViewById(R.id.tv_tv_A);
txtTitle.setText(itemA);
final TextView txtSubtitle = viewHolder.itemView.findViewById(R.id.tv_tv_B);
txtSubtitle.setText(itemB);
txtSubtitle.setVisibility(View.GONE);
//Add onPreDrawListener
txtSubtitle.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
txtSubtitle.getViewTreeObserver().removeOnPreDrawListener(this);
txtSubtitle.setVisibility(View.GONE);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
txtSubtitle.measure(widthSpec, heightSpec);
txtSubtitleHeight = txtSubtitle.getMeasuredHeight();
return true;
}
});
final TextView txtArrowTV = viewHolder.itemView.findViewById(R.id.tv_tv_expandcollapse);
txtArrowTV.setText(R.string.fa_icon_chevron_down);
txtArrowTV.setTypeface(iconFont);
//
CardView cardView = viewHolder.itemView.findViewById(R.id.cv_tv);
LinearLayout mLinearLayoutTV = viewHolder.itemView.findViewById(R.id.cardview_tv_titlerow);
//
cardView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(txtSubtitle.getVisibility() == View.GONE){
expandTV(txtSubtitle, txtArrowTV);
} else {
collapseTV(txtSubtitle, txtArrowTV);
}
}
});
mLinearLayoutTV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(txtSubtitle.getVisibility() == View.GONE){
expandTV(txtSubtitle, txtArrowTV);
} else {
collapseTV(txtSubtitle, txtArrowTV);
}
}
});
txtArrowTV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(txtSubtitle.getVisibility() == View.GONE){
expandTV(txtSubtitle, txtArrowTV);
} else {
collapseTV(txtSubtitle, txtArrowTV);
}
}
});
break;
case GRID_TYPE:
TextView titleG = viewHolder.itemView.findViewById(R.id.tv_gv_A);
titleG.setText("French vowels");
txtArrowGV = viewHolder.itemView.findViewById(R.id.tv_gv_expandcollapse);
txtArrowGV.setText(R.string.fa_icon_chevron_down);
txtArrowGV.setTypeface(iconFont);
final GridView mGridViewA = viewHolder.itemView.findViewById(R.id.gv);
mGridViewA.setVisibility(View.GONE);
mGridViewA.setEnabled(false);
mGridViewA.setVerticalScrollBarEnabled(false);
adapterGV = new MyFragmentRV.MyAdapter(getActivity().getApplicationContext(), 0);
mGridViewA.setAdapter(adapterGV);
for (String fVowel : fVowels) {
adapterGV.addAdapterItem(new MyFragmentRV.AdapterItem(fVowel));
}
//Add onPreDrawListener
mGridViewA.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
mGridViewA.getViewTreeObserver().removeOnPreDrawListener(this);
mGridViewA.setVisibility(View.GONE);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
mGridViewA.measure(widthSpec, heightSpec);
mGridViewHeight = mGridViewA.getMeasuredHeight();
return true;
}
});
CardView cardViewG = viewHolder.itemView.findViewById(R.id.cv_gv);
LinearLayout mLinearLayoutGV = viewHolder.itemView.findViewById(R.id.cardview_gv_titlerow);
cardViewG.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mGridViewA.getVisibility() == View.GONE){
expandGV(mGridViewA);
} else {
collapseGV(mGridViewA);
}
}
});
mLinearLayoutGV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mGridViewA.getVisibility() == View.GONE){
expandGV(mGridViewA);
} else {
collapseGV(mGridViewA);
}
}
});
txtArrowGV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mGridViewA.getVisibility() == View.GONE){
expandGV(mGridViewA);
} else {
collapseGV(mGridViewA);
}
}
});
break;
}
}
@Override
public int getItemCount() {
return data.size() + 3;
}
@Override
public int getItemViewType(int position) {
switch (position) {
case 0:
return HEADER_TYPE;
case 1:
return HEADER_TYPE_2;
case 2:
return GRID_TYPE;
default: return ITEM_TYPE;
}
}
};
}
private View inflateHelper(int resId, ViewGroup parent) {
return LayoutInflater.from(getActivity()).inflate(resId, parent, false);
}
class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(@NonNull View itemView) {
super(itemView);
}
}
public void expandGV() {
mGridViewA.setVisibility(View.VISIBLE);
txtArrowGV.setText(R.string.fa_icon_chevron_up);
ValueAnimator mAnimatorGV = slideAnimator(0, mGridViewHeight);
mAnimatorGV.start();
}
public void collapseGV() {
txtArrowGV.setText(R.string.fa_icon_chevron_down);
int finalGVHeight = mGridViewA.getHeight();
mAnimatorGV = slideAnimator(finalGVHeight, 0);
mAnimatorGV.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationEnd(Animator animator) {
mGridViewA.setVisibility(View.GONE);
}
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
mAnimatorGV.start();
}
public void expandTV() {
txtSubtitle.setVisibility(View.VISIBLE);
txtArrowTV.setText(R.string.fa_icon_chevron_up);
mAnimatorTV = slideAnimator(0, txtSubtitleHeight);
mAnimatorTV.start();
}
public void collapseTV() {
txtArrowTV.setText(R.string.fa_icon_chevron_down);
int finalTVHeight = txtSubtitle.getHeight();
mAnimatorTV = slideAnimator(finalTVHeight, 0);
mAnimatorTV.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationEnd(Animator animator) {
txtSubtitle.setVisibility(View.GONE);
}
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
mAnimatorTV.start();
}
public ValueAnimator slideAnimator(int start, int end, final View txtSubtitle) {
final ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
// update height
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParamsTV = txtSubtitle.getLayoutParams();
layoutParamsTV.height = value;
txtSubtitle.setLayoutParams(layoutParamsTV);
}
});
return animator;
}
/// Adapter for GridView
private class MyAdapter extends ArrayAdapter<MyFragmentRV.AdapterItem> {
private List<MyFragmentRV.AdapterItem> items = new ArrayList<>();
MyAdapter(Context context, int textviewid) {
super(context, textviewid);
}
void addAdapterItem(MyFragmentRV.AdapterItem item) {
items.add(item);
}
@Override
public int getCount() {
return items.size();
}
@Override
public MyFragmentRV.AdapterItem getItem(int position) {
return ((null != items) ? items.get(position) : null);
}
@Override
public long getItemId(int position) {
return position;
}
@NonNull
@Override
public View getView(final int position, View convertView, @NonNull final ViewGroup parent) {
View rowView;
if (convertView == null) {
rowView = getActivity().getLayoutInflater().inflate(R.layout.gridview_item, parent, false);
} else {
rowView = convertView;
}
TextView tv = rowView.findViewById(R.id.item_gridview);
tv.setText(items.get(position).first);
return rowView;
}
}
public class AdapterItem {
String first;
//add more items
AdapterItem(String first) {
this.first = first;
}
}
}
Based on Nikhil's suggestion
Upvotes: 0
Views: 76
Reputation: 1944
The problem here is that you are storing txtSubtitle
as a member variable of your Fragment. So when the GridView
is done initializing your grid items, txtSubtitle
will have the last assigned TextView, which in your case is Item C. So when you perform the animation, it always gets applied to Item C. The solution here is to make txtSubtitle
a local final
Variable. Remove TextView txtArrowGV, txtArrowTV, txtTitle, txtSubtitle;
from MyFragmentRV. In case ITEM_TYPE
:
final TextView txtSubtitle = viewHolder.itemView.findViewById(R.id.tv_tv_B);
....
cardView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(txtSubtitle.getVisibility() == View.GONE){
expandTV(txtSubtitle);
} else {
collapseTV(txtSubtitle);
}
}
});
pass txtSubtitle
to expandTV
, collapseTV
and in expandTV
:
public void expandTV(TextView txtSubtitle) {
txtSubtitle.setVisibility(View.VISIBLE);
txtArrowTV.setText(R.string.fa_icon_chevron_up);
mAnimatorTV = slideAnimator(0, txtSubtitleHeight);
mAnimatorTV.start();
}
EDIT: change slide animator function as:
public ValueAnimator slideAnimator(int start, int end,TextView txtSubtitle) {
final ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
// update height
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParamsTV = txtSubtitle.getLayoutParams();
layoutParamsTV.height = value;
txtSubtitle.setLayoutParams(layoutParamsTV);
}
});
return animator;
}
and call it like this:
mAnimatorTV = slideAnimator(0, txtSubtitleHeight,txtSubtitle);
mAnimatorTV.start();
Upvotes: 2