I have a ListView
and I use the following layout for the adapter:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/llContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:orientation="vertical" >
<TextView
android:id="@+id/tvLocal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="question"
android:textColor="@color/app_gray"
android:textSize="12sp" >
</TextView>
<TextView
android:id="@+id/tvQuestion"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="question"
android:textColor="@color/app_gray"
android:textSize="12sp"
android:textStyle="bold" >
</TextView>
<LinearLayout
android:id="@+id/llContainerLikeDislike"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:orientation="vertical"
android:visibility="gone" >
<TextView
android:id="@+id/tvAnswer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:textColor="@color/app_blue"
android:textSize="12sp" />
<LinearLayout
android:id="@+id/llContainerButtons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:orientation="horizontal" >
<LinearLayout
android:id="@+id/llLike"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:padding="5dp"
android:clickable="true"
android:gravity="center_horizontal"
android:orientation="vertical" >
<ImageView
android:id="@+id/ivLike"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:layout_margin="5dp"
android:src="@drawable/background_btn_like" />
<TextView
android:id="@+id/tvHelped"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Helped"
android:textColor="@color/app_gray"
android:textSize="12sp" >
</TextView>
<TextView
android:id="@+id/tvNumLike"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/app_gray"
android:textSize="12sp" >
</TextView>
</LinearLayout>
<LinearLayout
android:id="@+id/llDisLike"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:padding="5dp"
android:clickable="true"
android:gravity="center_horizontal"
android:orientation="vertical" >
<ImageView
android:id="@+id/ivDisLike"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:layout_margin="5dp"
android:src="@drawable/background_btn_dislike" />
<TextView
android:id="@+id/tvNotHelp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Not Help"
android:textColor="@color/app_gray"
android:textSize="12sp" >
</TextView>
<TextView
android:id="@+id/tvNumDisLike"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/app_gray"
android:textSize="12sp" >
</TextView>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
QA class:
public class QA {
private int id;
private String question, answer, answer_at, state, city, name;
public enum state { like, dislike };
private long up_votes_count, down_votes_count;
private boolean visible, like, wasVoted;
// get and setters
}
In case llContainer ( LinearLayout
) should receive a click event and display the LinearLayout
llContainerLikeDislike, as an expandable list. Control whether llContainerLikeDislike is with its Visibility Gone
passed to Visible
and vice versa. I use this method for this:
public static void expand(final View v, final ListView lv, final int position) {
v.measure(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
final int targetHeight = v.getMeasuredHeight();
v.getLayoutParams().height = 0;
v.setVisibility(View.VISIBLE);
Animation a = new Animation()
{
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
v.getLayoutParams().height = interpolatedTime == 1
? LayoutParams.WRAP_CONTENT
: (int)(targetHeight * interpolatedTime);
v.requestLayout();
//Moves the listview scroll so that the expanding area is visible.
lv.setSelectionFromTop(position, v.getLayoutParams().height);
}
@Override
public boolean willChangeBounds() {
return true;
}
};
// 1dp/ms
a.setDuration((int)(targetHeight / v.getContext().getResources().getDisplayMetrics().density));
v.startAnimation(a);
}
public static void collapse(final View v) {
final int initialHeight = v.getMeasuredHeight();
Animation a = new Animation()
{
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if(interpolatedTime == 1){
v.setVisibility(View.GONE);
}else{
v.getLayoutParams().height = initialHeight - (int)(initialHeight * interpolatedTime);
v.requestLayout();
}
}
@Override
public boolean willChangeBounds() {
return true;
}
};
// 1dp/ms
a.setDuration((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density));
v.startAnimation(a);
}
No Adapter
use ViewHolder
to control the reference of each line with setTag()
and getTag()
. In case each line is correctly maintaining the data of the object qa during the scroll.
However the scroll is changing the behavior of the lines that control the expansion and retraction of the lines and, enabling and disabling a line that had its ImageView
selected.
I use ImageView
's as if it were buttons to give a like or dislike in a row, when clicked it is disabled and it changes the appearance.
In short, the data in each row is correct for the objects qa but that of the components such as Visibility Gone/Visible
line, color change, enable and disable during ListView
scroll are misbehaving.
If a line for example of position 0 is Visible
, disabled, and has changed color, moving the scroll of ListView
a random line below has the same status as line 0.
I used the debug and the methods are getting the reference correctly and, being called only once, there is no repetition for the random lines.
Follow getView()
of adapter:
public class QAAdapter extends ArrayAdapter<QA> implements Filterable {
private List<QA>filteredData;
private ArrayList<QA> arrayQA;
private ViewHolder holder;
private final LayoutInflater inflater;
private Activity activity;
public QAAdapter(Activity activity,
ArrayList<QA> arrayQA) {
super(activity, 0);
this.inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.arrayQA = arrayQA;
this.filteredData = arrayQA;
this.activity = activity;
}
public View getView(final int position, View convertView,
final ViewGroup parent) {
holder = new ViewHolder();
if (convertView == null) {
convertView = inflater.inflate(R.layout.item_pergunta, parent,
false);
Typeface tfLight = Typeface.createFromAsset(activity.getAssets(),
"fonts/Oswald-Light.ttf");
Typeface tfBold = Typeface.createFromAsset(activity.getAssets(),
"fonts/Oswald-Bold.ttf");
holder.tvAjudou = (TextView) convertView
.findViewById(R.id.tvAjudou);
holder.tvNaoAjudou = (TextView) convertView
.findViewById(R.id.tvNaoAjudou);
holder.tvLocal = (TextView) convertView.findViewById(R.id.tvLocal);
holder.tvLocal.setTypeface(tfLight);
holder.tvQuestion = (TextView) convertView
.findViewById(R.id.tvQuestion);
holder.tvQuestion.setTypeface(tfBold);
holder.tvAnswer = (TextView) convertView
.findViewById(R.id.tvAnswer);
holder.tvAnswer.setTypeface(tfLight);
holder.tvNumLike = (TextView) convertView
.findViewById(R.id.tvNumLike);
holder.tvNumLike.setTypeface(tfLight);
holder.tvNumDisLike = (TextView) convertView
.findViewById(R.id.tvNumDisLike);
holder.tvNumDisLike.setTypeface(tfLight);
holder.ivLike = (ImageView) convertView.findViewById(R.id.ivLike);
holder.ivDisLike = (ImageView) convertView
.findViewById(R.id.ivDisLike);
holder.llContainerButtons = (LinearLayout) convertView
.findViewById(R.id.llContainerButtons);
holder.llContainer = (LinearLayout) convertView
.findViewById(R.id.llContainer);
holder.llContainerLikeDislike = (LinearLayout) convertView
.findViewById(R.id.llContainerLikeDislike);
holder.llContainerLikeDislike.setVisibility(View.GONE);
holder.llLike = (LinearLayout) convertView
.findViewById(R.id.llLike);
holder.llDisLike = (LinearLayout) convertView
.findViewById(R.id.llDisLike);
holder.llContainer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final int position2 = ((ListView) parent)
.getPositionForView(v);
QA qa = getItemQa(position2);
boolean visibility = qa.isVisible();
if (!visibility) {
qa.setVisible(true);
UtilsLab.expand(
v.findViewById(R.id.llContainerLikeDislike),
((ListView) parent), position2);
} else {
qa.setVisible(false);
UtilsLab.collapse(v
.findViewById(R.id.llContainerLikeDislike));
}
QASelected selectQASelectedById = QASelectedModel
.selectQASelectedById(activity, qa.getId());
if (selectQASelectedById != null) {
v.findViewById(R.id.llLike).setEnabled(false);
v.findViewById(R.id.llDisLike).setEnabled(false);
boolean isLike = selectQASelectedById.isLike();
isLike = qa.isLike();
qa.setWasVoted(true);
v.findViewById(R.id.llLike).setSelected(isLike);
v.findViewById(R.id.llDisLike).setSelected(!isLike);
ImageView ivLike = (ImageView) v
.findViewById(R.id.ivLike);
ivLike.setSelected(isLike);
ImageView ivDisLike = (ImageView) v
.findViewById(R.id.ivDisLike);
ivDisLike.setSelected(!isLike);
if (isLike) {
((TextView) v.findViewById(R.id.tvAjudou))
.setTextColor(activity.getResources()
.getColor(R.color.app_azul_claro));
((TextView) v.findViewById(R.id.tvNumLike))
.setTextColor(activity.getResources()
.getColor(R.color.app_azul_claro));
((TextView) v.findViewById(R.id.tvNaoAjudou))
.setTextColor(activity.getResources()
.getColor(R.color.app_cinza_claro));
((TextView) v.findViewById(R.id.tvNumDisLike))
.setTextColor(activity.getResources()
.getColor(R.color.app_cinza_claro));
} else {
((TextView) v.findViewById(R.id.tvAjudou))
.setTextColor(activity.getResources()
.getColor(R.color.app_cinza_claro));
((TextView) v.findViewById(R.id.tvNumLike))
.setTextColor(activity.getResources()
.getColor(R.color.app_cinza_claro));
((TextView) v.findViewById(R.id.tvNaoAjudou))
.setTextColor(activity.getResources()
.getColor(R.color.app_vermelho));
((TextView) v.findViewById(R.id.tvNumDisLike))
.setTextColor(activity.getResources()
.getColor(R.color.app_vermelho));
}
}
notifyDataSetChanged();
}
});
holder.llLike.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
QA qa = getItemQa(position);
int value = (int) (qa.getUp_count() + 1);
qa.setUp_count(value);
qa.setLike(true);
qa.setWasVoted(true);
((LinearLayout) v).setEnabled(false);
((LinearLayout) ((LinearLayout) v.getParent())
.findViewById(R.id.llDisLike)).setEnabled(false);
((ImageView) v.findViewById(R.id.ivLike)).setSelected(true);
((TextView) v.findViewById(R.id.tvAjudou))
.setTextColor(activity.getResources().getColor(
R.color.app_azul_claro));
((TextView) v.findViewById(R.id.tvNumLike))
.setTextColor(activity.getResources().getColor(
R.color.app_azul_claro));
notifyDataSetChanged();
TrackingManager.trackEvent(ConstantsLab.VOTACAO,
ConstantsLab.EVENTO_RESPOSTA_POSITIVA,
qa.getQuestion());
UpDownVoteWsAsyncTask asyncTask = new UpDownVoteWsAsyncTask();
asyncTask.flagUPDown = 1;
asyncTask.id = qa.getId();
asyncTask.execute(ConstantsLab.WS_BASE_URL
+ ConstantsLab.WS_UP_DOWN_VOTE);
}
});
holder.llDisLike.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
QA qa = getItemQa(position);
int value = (int) (qa.getDown_count() + 1);
qa.setDown_count(value);
qa.setLike(false);
qa.setWasVoted(true);
((LinearLayout) v).setEnabled(false);
((LinearLayout) ((LinearLayout) v.getParent())
.findViewById(R.id.llLike)).setEnabled(false);
((ImageView) v.findViewById(R.id.ivDisLike))
.setSelected(true);
((TextView) v.findViewById(R.id.tvNaoAjudou))
.setTextColor(activity.getResources().getColor(
R.color.app_vermelho));
((TextView) v.findViewById(R.id.tvNumDisLike))
.setTextColor(activity.getResources().getColor(
R.color.app_vermelho));
notifyDataSetChanged();
TrackingManager.trackEvent(ConstantsLab.VOTACAO,
ConstantsLab.EVENTO_RESPOSTA_NEGATIVA,
qa.getQuestion());
UpDownVoteWsAsyncTask asyncTask = new UpDownVoteWsAsyncTask();
asyncTask.flagUPDown = 0;
asyncTask.id = qa.getId();
asyncTask.execute(ConstantsLab.WS_BASE_URL
+ ConstantsLab.WS_UP_DOWN_VOTE);
}
});
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final QA qa = getItemQa(position);
if (qa != null) {
String name = qa.getName();
String city = qa.getCity();
String state = qa.getState();
if (qa.getName().equals(null) || qa.getName().equals("null")) {
name = " ";
}
if (qa.getCity().equals(null) || qa.getCity().equals("null")) {
city = " ";
}
if (qa.getState().equals(null) || qa.getState().equals("null")) {
state = " ";
}
holder.tvLocal.setText(activity.getString(
R.string.local_item_pergunta, name, city, state));
holder.tvQuestion.setText(qa.getQuestion());
holder.tvAnswer.setText(qa.getAnswer());
holder.tvNumLike.setText(String.valueOf(qa.getUp_count()));
holder.tvNumDisLike.setText(String.valueOf(qa.getDown_count()));
if (qa.isVisible()) {
holder.llContainerLikeDislike.setVisibility(View.VISIBLE);
holder.llContainerLikeDislike.requestLayout();
} else {
holder.llContainerLikeDislike.setVisibility(View.GONE);
holder.llContainerLikeDislike.requestLayout();
}
if (qa.isWasVoted()) {
holder.llLike.setEnabled(false);
holder.llDisLike.setEnabled(false);
verifyStatusButtonLikeDisLike(qa);
} else {
holder.llLike.setEnabled(true);
holder.llDisLike.setEnabled(true);
holder.ivLike.setSelected(false);
holder.tvAjudou.setTextColor(activity.getResources().getColor(
R.color.app_cinza_claro));
holder.tvNumLike.setTextColor(activity.getResources().getColor(
R.color.app_cinza_claro));
holder.ivDisLike.setSelected(false);
holder.tvNaoAjudou.setTextColor(activity.getResources()
.getColor(R.color.app_cinza_claro));
holder.tvNumDisLike.setTextColor(activity.getResources()
.getColor(R.color.app_cinza_claro));
}
}
return convertView;
}
private void verifyStatusButtonLikeDisLike(final QA qa) {
if (!qa.isLike()) {
holder.ivDisLike.setSelected(true);
holder.tvNaoAjudou.setTextColor(activity.getResources().getColor(
R.color.app_vermelho));
holder.tvNumDisLike.setTextColor(activity.getResources().getColor(
R.color.app_vermelho));
holder.ivLike.setSelected(false);
holder.tvAjudou.setTextColor(activity.getResources().getColor(
R.color.app_cinza_claro));
holder.tvNumLike.setTextColor(activity.getResources().getColor(
R.color.app_cinza_claro));
} else {
holder.ivLike.setSelected(true);
holder.tvAjudou.setTextColor(activity.getResources().getColor(
R.color.app_azul_claro));
holder.tvNumLike.setTextColor(activity.getResources().getColor(
R.color.app_azul_claro));
holder.ivDisLike.setSelected(false);
holder.tvNaoAjudou.setTextColor(activity.getResources().getColor(
R.color.app_cinza_claro));
holder.tvNumDisLike.setTextColor(activity.getResources().getColor(
R.color.app_cinza_claro));
}
}
//another methods
//...
/**
* {@link AsyncTask} for the send data like or dislike.
*/
public class MyAsyncTask extends AsyncTask<String, Integer, Boolean>{...}
}