Android ListView OnItemClick event fires sometimes, but not always - android

I have a very wierd problem with the Android ListView component.
Situation: I'm basically utilizing the setOnItemClickListener method and subsequently overriding the "onItemClick" method to implement a custom action when clicking on an item in the listview.
Problem: The onItemClick event fires as it should on an item tap, however having tapped a decent amount of items (20-30-40), suddenly one, and only one, of the items become un-tapable, meaning nothing happens when you tap it. Meanwhile, all the other items are still tapable.
I have overridden the ListView component to capture the "dispatchTouchEvent" (see code below), and that reveals that when an item suddenly becomes un-tapable, the dispatchTouchEvent is indeed still fired, with reasonable X and Y coordinates, but somehow the "onItemClick" event failes to fire.
public class ExtendedListView extends ListView
{
public ExtendedListView(Context context) {
super(context);
}
#Override
public boolean dispatchTouchEvent(MotionEvent ev)
{
Log.v("dispatchTouchEvent", "click|X:" + ev.getX() + "|Y:" + ev.getY());
return super.dispatchTouchEvent(ev);
}
}
Method that initiates the ListView:
#SuppressWarnings("unchecked")
private void renderHomeScreenItemsList() {
HomeScreenItemAdapter m_adapter;
ArrayList<HomeScreenItem> lHomeScreenItems = null;
LayoutParams lpHomeScreenItems = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
lpHomeScreenItems.leftMargin = Util.dipsToPx(0, this);
lpHomeScreenItems.topMargin = Util.dipsToPx(155, this);
lpHomeScreenItems.gravity = 3; //3 = LEFT. Some gravity is necessary.
lvHomeScreenItems = new ExtendedListView(this);
lvHomeScreenItems.setLayoutParams(lpHomeScreenItems);
ColorDrawable sage = new ColorDrawable(this.getResources().getColor(R.color.HomeScreenDividerColor));
lvHomeScreenItems.setDivider(sage);
lvHomeScreenItems.setDividerHeight(1);
lvHomeScreenItems.setCacheColorHint(Color.WHITE);
lvHomeScreenItems.setBackgroundColor(Color.WHITE);
lHomeScreenItems = new ArrayList<HomeScreenItem>();
Iterator<EnabledSection> ite = Global.EnabledSectionsVector.iterator();
while (ite.hasNext()) {
EnabledSection es = (EnabledSection) ite.next();
HomeScreenItem h1 = new HomeScreenItem();
h1.setItemText(es.FunctionTitle);
h1.setFunctionName(es.FunctionName);
int imageId = getResources().getIdentifier("homescreen_" + es.FunctionName.toLowerCase(), "drawable", "XXXXXXXXX");
h1.setItemImageResourceId(imageId);
lHomeScreenItems.add(h1);
}
HomeScreenItem hCopyright = new HomeScreenItem();
hCopyright.setItemText("copyright");
hCopyright.setItemImageResourceId(-1);
lHomeScreenItems.add(hCopyright);
m_adapter = new HomeScreenItemAdapter(this, R.layout.row, lHomeScreenItems);
lvHomeScreenItems.setAdapter(m_adapter);
lvHomeScreenItems.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Log.v("onItemClick", "Click Detected");
HomeScreenItem hmi = (HomeScreenItem)parent.getItemAtPosition(position);
Boolean blnAcceptClick = true;
if (WrapperWebView == null)
blnAcceptClick = true;
else
if (WrapperWebView.getProgress() != 100)
blnAcceptClick = false;
if (hmi.getItemImageResourceId() != -1 && blnAcceptClick)
{
Global.IsLoadingWrapper = true;
vSelectedItem = view;
ImageView ivIcon = (ImageView)view.findViewById(R.id.rightarrow);
LinearLayout ll = (LinearLayout)view.findViewById(R.id.RowContainer);
TextView ll2 = (TextView)view.findViewById(R.id.toptext);
ll.setBackgroundResource(R.color.BlueActive);
ll2.setBackgroundResource(R.color.BlueActive);
ll2.setTextColor(Color.WHITE);
int[] intCoordinates = new int[2];
view.getLocationInWindow(intCoordinates);
lpLoadingSpinner.topMargin = intCoordinates[1] - Util.dipsToPx(10, HomeScreen.this);
ivLoadingSpinner.setVisibility(View.VISIBLE);
ivLoadingSpinner.bringToFront();
LoadingSpinnerAnimation.start();
ivIcon.setVisibility(View.GONE);
ivClickedItemIcon = ivIcon;
InitializeWebView(hmi.getFunctionName());
}
}
});
}
Any help would be greatly appeciated. The bug is confirmed to happen on multiple devices and on multiple android versions.
UPDATE: Problem fixed
I've managed to find the problem and fix it. Basically, the list of items contained all "normal" items (in my context), except for the bottom one, which was a "copyright" item with a link (in blue) that had to be clickable. To get this click effect working, I used:
tt.setMovementMethod(LinkMovementMethod.getInstance());
if the item being rendered was the copyright item.
However, I didn't counter that effect if the item being rendered was a "normal" item, so by only adding the line below when a normal item is rendered, the problem went away:
tt.setMovementMethod(null);
Full example code (with non-relevant code removed)
public class HomeScreenItemAdapter extends ArrayAdapter<HomeScreenItem> {
private ArrayList<HomeScreenItem> items;
public HomeScreenItemAdapter(Context context, int textViewResourceId, ArrayList<HomeScreenItem> items) {
super(context, textViewResourceId, items);
this.items = items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
HomeScreenItem o = items.get(position);
if (o != null) {
TextView tt = (TextView) v.findViewById(R.id.toptext);
if (iv != null && tt != null)
{
if (position != (items.size() - 1))
{
tt.setMovementMethod(null);
}
else
{
//last element, so setup element to show copyright notice instead
tt.setMovementMethod(LinkMovementMethod.getInstance()); //this makes the link clickable
}
}
}
return v;
}
}

Related

The views are crazy, I use ListView. And all views change

I have a code that every view will have a different background
When I scroll up and down then all the colors get confused and all the CheckBoxes markings change and when I erase a part, Then something else is erased and changed color.
tell me if you need The full code
this the code of Changed Background color:
View v = List.getChildAt(index -
List.getFirstVisiblePosition());
if(v == null)
return;
ConstraintLayout LayoutT = v.findViewById(R.id.LayoutT);
LayoutT.setBackgroundColor(Color.RED);
this code of delete:
CheckBox currentCheckBox = (CheckBox) view.findViewById(mCheckBoxView);
currentCheckBox.setChecked(mCheckBoxStates[csr.getPosition()]);
currentCheckBox.setTag(new Long(csr.getLong(csr.getColumnIndex(DatabaseHandler.KEY_ID))));
currentCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
final int position = fcsr.getPosition();
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mCheckBoxStates[position] = ((CheckBox) buttonView).isChecked();
int restore_cursor_position = fcsr.getPosition();
fcsr.moveToPosition(position);
fcsr.moveToPosition(restore_cursor_position); //restore the Cursor's position
}
});
another class:
for (long id : mCheckedIdList) {
mDBHandler.deleteRecord(id);
}
db.beginTransaction();
db.setTransactionSuccessful();
db.endTransaction();
mCsr = mDBHandler.getAllRecords();
mMCA.swapCursor(mCsr);
EDIT
I tried to do what you suggested at first. And it worked perfectly but when I adjusted it to my code then only one color appears, it changes depending on the last color or the first one that should appear
#Override
public View getView(int position, View view, ViewGroup parent) {
View convertview = super.getView(position, view,parent);
parent.setBackgroundColor(0xFF555555);
// LinearLayout ll = (LinearLayout)
convertview.findViewById(R.id.item_layout);
int[] colours = new int[]{
ContextCompat.getColor(mContext, R.color.Cyan)
, ContextCompat.getColor(mContext, R.color.LowRed)
};
if (convertview != null) {
DatabaseHandler db = new DatabaseHandler(mContext);
int Count = List.getCount();
String[] Status = new String[Count];
for (int i = 0; i < Count; ) {
Cursor c = db.getCursorStatus(i);
if (c != null) {
while (c.moveToNext()) {
Status[i] = c.getString(0);
}
}
String mStatus[] = new String[3];
mStatus[0] = "Monetary_Expenditure";
mStatus[1] = "Financial income";
mStatus[2] = "Cancellation";
boolean first = Status[i] != null && Status[i].equals(mStatus[1]);
boolean second = Status[i] != null && Status[i].equals(mStatus[0]);
if (Status[i] != null && Status[i].equals(mStatus[1])) {
convertview.setBackgroundColor(colours[1]);
} else if (Status[i] != null && Status[i].equals(mStatus[0])) {
convertview.setBackgroundColor(colours[0]);
}
else {convertview.setBackgroundColor(0xFF555555);}
i++;
}
}
return convertview;
}
edit 2
I changed it:
if (convertview != null) {
Cursor csr = getCursor();
String status = csr.getString(0);
int colourindex = 0;
if (status.equals("Financial income") {
colourindex = 1;
}
if (status.equals("Cancellation") {
colourindex = 2;
}
convertview.setBackgroundColor(colours[colourindex]);
}
to:
if (convertview != null) {
Cursor csrF = getCursor();
DatabaseHandler db = new DatabaseHandler(mContext);
i++;
Cursor csr = db.getCursorStatus(i);
String status = csr.getString(0);
int colourindex = 0;
if (status.equals("Financial income")) {
colourindex = 1;
}
if (status.equals("Cancellation")) {
colourindex = 2;
}
convertview.setBackgroundColor(colours[colourindex]);
}
Because the cursor did not turn to the right line
And I get this error: android.database.CursorIndexOutOfBoundsException: Index -1 requested, with a size of 1
EDIT 3
Cursor csr = getCursor();
String status="";
while (csr.moveToNext()) {
status = csr.getString(4);
}
int colourindex = 3;
if ("Monetary Expenditure".equals(status)){
colourindex = 0;
}
else if ("Financial income".equals(status)) {
colourindex = 1;
}
else if ("Cancellation".equals(status)) {
colourindex = 2;
}
convertview.setBackgroundColor(colours[colourindex]);
Log.i("Debug", "colour index is: "+colourindex);
In your Custom Adapter override the getView() method and set the colour in that method.
The signature is :- public View getView(int position, View view, ViewGroup parent).
position is the position within the list of the view being processed.
view is the view (the layout)
parent is the ListView
The modified view should be returned.
An example, that changes the background colour (every x rows is a different colour where x is the number of colours) :-
#Override
public View getView(int position, View view, ViewGroup parent) {
View convertview = super.getView(position, view,parent);
parent.setBackgroundColor(0xFF555555);
//LinearLayout ll = (LinearLayout) convertview.findViewById(R.id.item_layout);
int[] colours = new int[]{0xFFFF55FF,0xBBFF55FF,0x77FF55FF};
if (convertview != null) {
convertview.setBackgroundColor(colours[position % colours.length]);
//ll.setBackgroundColor(colours[position % colours.length]);
}
return convertview;
}
parent.setBackgroundColor(0xFF555555); sets the background colour (darkish grey) of the ListView.
Commented out lines are an alternative means to get get the layout but aren't required
In this example the colours are the same but the transparency is increases thus letting the background (ListView/parent) colour to show.
The result (as per modification of the code provided in the answer provided to your previous question) :-
You could alternately override the bindView method, as that has the View passed to it, although it does not have the position passed. You could also use a combination of both.
Another Example of manipulating ListView colours
Here's a completer Custom Cursor Adapter that uses user defined colours extracted from the database to colour each item accordingly using bindView. However, this changes the drawable's background colour. It's for Credit Card management (work still in progress ), thus each item in the ListView looks like a Credit Card. :-
public class CardListViewCursorAdapter extends CursorAdapter {
Context mContext;
Cursor mCurssor;
CardListViewCursorAdapter(Context context, Cursor csr) {
super(context, csr, 0);
mContext = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
return super.getView(position, convertView, parent);
}
#Override
public View newView(Context context, Cursor csr, ViewGroup viewGroup) {
return LayoutInflater.from(context).inflate(
R.layout.standard_cards_listview,
viewGroup,
false
);
}
#Override
public void bindView(View view, Context context, Cursor csr) {
TextView mCardName = view.findViewById(R.id.scl_cardname);
TextView mCardNotes = view.findViewById(R.id.scl_cardnotes);
LinearLayout mCardLayout = view.findViewById(R.id.scl_cardlayout);
Drawable mDrawable = mCardLayout.getBackground();
int cardcolour = (int) CardManageActivity.DEFAULT_CARDCOLOUR;
long extracted_cardcolour = csr.getLong(
csr.getColumnIndex(
DBCardsTableConstants.CARDCOLOUR.getDBColumnName()
)
);
if ((extracted_cardcolour & DBCardsTableConstants.CARDSCOLOUR_FLAG) > 0) {
cardcolour = (int) extracted_cardcolour;
}
setShapeBackGroundColour(mDrawable, cardcolour);
mCardName.setText(
csr.getString(
csr.getColumnIndex(
DBCardsTableConstants.CARDNAME.getDBColumnName())
)
);
mCardNotes.setText(
csr.getString(
csr.getColumnIndex(
DBCardsTableConstants.CARDNOTES.getDBColumnName()
)
)
);
}
private void setShapeBackGroundColour(Drawable drawable, int colour) {
if (drawable instanceof ShapeDrawable) {
ShapeDrawable sd = (ShapeDrawable) drawable;
sd.getPaint().setColor(colour);
}
if (drawable instanceof GradientDrawable) {
GradientDrawable gd = (GradientDrawable) drawable;
gd.setColor(colour);
}
if (drawable instanceof ColorDrawable) {
ColorDrawable cd = (ColorDrawable) drawable;
cd.setColor(colour);
}
}
}
This looks like :-
Answer regarding :-
I edited the code as you said I tried to do what you suggested at
first. And it worked perfectly but when I adjusted it to my code then
only one color appears, it changes depending on the last color or the
first one that should appear And why what I tried was problems? And
thank you very much for everything
I believe that your problem is that within getView (which is called individually for each item in the list (as is bindView) the View being the View for that position in the list) you are the looping through all the items/views and basically changing ALL items to be as per the last item.
i.e. getView will be called (at least) 10 times for a list of 10 items. Each time it is called it is for the individual view/item being processed.
I believe you need to change :-
if (convertview != null) {
DatabaseHandler db = new DatabaseHandler(mContext);
int Count = List.getCount();
String[] Status = new String[Count];
for (int i = 0; i < Count; ) {
Cursor c = db.getCursorStatus(i);
if (c != null) {
while (c.moveToNext()) {
Status[i] = c.getString(0);
}
}
String mStatus[] = new String[3];
mStatus[0] = "Monetary_Expenditure";
mStatus[1] = "Financial income";
mStatus[2] = "Cancellation";
boolean first = Status[i] != null && Status[i].equals(mStatus[1]);
boolean second = Status[i] != null && Status[i].equals(mStatus[0]);
if (Status[i] != null && Status[i].equals(mStatus[1])) {
convertview.setBackgroundColor(colours[1]);
} else if (Status[i] != null && Status[i].equals(mStatus[0])) {
convertview.setBackgroundColor(colours[0]);
}
else {convertview.setBackgroundColor(0xFF555555);}
i++;
}
}
To :-
if (convertview != null) {
Cursor csr = getCursor();
String status = csr.getString(0);
int colourindex = 0;
if (status.equals("Financial income") {
colourindex = 1;
}
if (status.equals("Cancellation") {
colourindex = 2;
}
convertview.setBackgroundColor(colours[colourindex]);
}
So you get the cursor using the CursorAdapters getCursor() method, the cursor will be positioned accordingly.
You then extract the status for the item in the List for which the View is being built i.e. getView() is called for every item in the list. So if you have 10 items in the List getView() is called at least 10 times. You appear to consider that it is called just once.
The colour index (0,1 or 2) is determined according to the status (column 0).
The background colour is set according to the status for the specific Item in the list.

Sum method called multiple times in getview

I have a method that add days in the date of the listview, the problem is when I scroll down and up again the values are changed, I have 2 kinds of date, so the method has to be called when the second kind appears in the list, but how can I use the method once so it doesn't keep adding each time I scroll it?
public class FaturasAdapter extends ArrayAdapter<Faturas> {
private Activity activity;
private LayoutInflater inflater;
private List<Faturas> faturasItens;
private Ferramentas mFerramentas;
private String entrada;
private String parcela = "";
private Map<Integer, Integer> intervaloMap;
private String data ="";
public FaturasAdapter(Activity activity, RealmList<Faturas> inItems) {
super(activity, R.layout.faturas_adapter, inItems);
this.activity = activity;
this.faturasItens = inItems;
this.mFerramentas = new Ferramentas();
this.intervaloMap = new HashMap<Integer, Integer>();
}
#Override
public int getCount() {
return faturasItens.size();
}
#Override
public Faturas getItem(int location) {
return faturasItens.get(location);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View vi = convertView;
final ViewHolder holder;
final Faturas mFaturas = faturasItens.get(position);
if (inflater == null)
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
vi = inflater.inflate(R.layout.faturas_adapter, null);
holder = new ViewHolder();
holder.numero = (TextView) vi.findViewById(R.id.numero);
holder.intervalo = (TextView) vi.findViewById(R.id.intervalo);
holder.valor = (TextView) vi.findViewById(R.id.valor);
holder.data = (TextView) vi.findViewById(R.id.data);
vi.setTag(holder);
} else {
holder = (ViewHolder) vi.getTag();
}
addIntervalo(mFaturas.getIntervalo(), mFaturas.getTipo());
data = mFerramentas.dataText(intervaloMap.get(mFaturas.getTipo()));
holder.intervalo.setText(String.valueOf(mFaturas.getIntervalo()));
if (mFaturas.getTipo() == Faturas.intervaloConstanteEntrada) {
entrada = "ENT - ";
holder.numero.setText(entrada + mFaturas.getOrder() + "/" + mFaturas.getQtParcelasEntrada());
holder.valor.setText(String.valueOf(mFaturas.getValor()) + " ");
} else {
parcela = "PAR - ";
holder.numero.setText(parcela + mFaturas.getOrder() + "/" + mFaturas.getQtParcela());
holder.valor.setText(String.valueOf(mFaturas.getValor()) + " ");
}
holder.data.setText(data);
return vi;
}
private void addIntervalo(int intervalo, int tipo) {
int intervaloSum = intervalo;
if (!intervaloMap.isEmpty()) {
if (intervaloMap.get(tipo) != null)
intervaloSum += intervaloMap.get(tipo);
}
intervaloMap.put(tipo, intervaloSum);
}
public List<Faturas> getfaturasItens() {
return faturasItens;
}
public void setData(List<Faturas> fat) {
this.faturasItens.addAll(fat);
this.notifyDataSetChanged();
}
public class ViewHolder {
TextView numero;
TextView intervalo;
TextView data;
TextView valor;
}
}
As I said in my comment I don't see why do you need to call the addIntervalo() method in the getView() method. The problem with this is that getView() will be called a lot as the user uses the ListView so you'll end up adding the same data again and again.
From your code it seems you just show the data calculated with addIntervalo()(I'm assuming each item will present its data relative to the total that you calculate for that type that you calculate with addIntervalo()) so you could simply calculate in advance the values and then in getView() simply use that.
// in the constructor you get the data so calculate the values
// iterating over the data
public FaturasAdapter(Activity activity, RealmList<Faturas> inItems) {
super(activity, R.layout.faturas_adapter, inItems);
this.activity = activity;
this.faturasItens = inItems;
this.mFerramentas = new Ferramentas();
this.intervaloMap = new HashMap<Integer, Integer>();
foreach(Faturas f : inItems) {
addIntervalo(f.getIntervalo(), f.getTipo());
}
}
You also have the setData() method where you update the data list so you also need to calculate the result of addIntervalo() for the new items that are about to be added to the adapter:
public void setData(List<Faturas> fat) {
// because you're adding the fat list to the current set of items
// simply calculate addIntervalo() for them to add their count to the total
foreach(Faturas f : fat) {
addIntervalo(f.getIntervalo(), f.getTipo());
}
this.faturasItens.addAll(fat);
this.notifyDataSetChanged();
}
In getView() remove the line:
addIntervalo(mFaturas.getIntervalo(), mFaturas.getTipo());
as you already calculated the values.
Oh, it's problem for base android list's widget to change view. You should know, that ListView and RecycleView caching and invalidating views per scrolling! So your views will be changed to default type!
RecycleView has solution, it's using several type from method getItemType(). But you work with ListView. Anyway! Better solution it's use special list widget which supports custom views and changing that views in anytime. For this task use LinkedListView!

ArrayAdapter strange behaviour with id when items's height exceed Listview height

I'm facing a strange behaviour using an ArrayAdapter.
When the number of listview item exceed the height of the listView (say after item 8), the next item get the id 0 instead the id 9.
In my opinion this type of issue was explained here with the convertView, but i use it in the same way (i think).
The following code is my ArrayAdapter.
public class StepsAdapter extends ArrayAdapter<String> {
Context context;
List<String> steps;
public StepsAdapter(Context context, int resourceId, List<String> steps) {
super(context, resourceId, steps);
this.context = context;
}
private class ViewHolder {
EditText stepValue;
ImageView removeStep;
}
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
final String step = getItem(position);
LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
convertView = mInflater.inflate(R.layout.row_step, null);
holder = new ViewHolder();
holder.stepValue = (EditText) convertView.findViewById(R.id.stepEdit);
holder.removeStep = (ImageView) convertView.findViewById(R.id.removeStep);
holder.stepValue.setText(step);
holder.removeStep.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context,"* Remove id step " + position, Toast.LENGTH_LONG).show();
steps.remove(position);
notifyDataSetChanged();
}
});
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
return convertView;
}
}
Then my main activity where i get existing data and put it in my listView, the add button and the save button.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.add_game);
mContext = getApplicationContext();
steps = new ArrayList<String>();
stepsAdapter = new StepsAdapter(mContext,R.layout.row_step,steps);
Gson gson = new GsonBuilder().create();
game = gson.fromJson(gameJson, Games.class);
/*
* Settings values
*/
gameNameValue.setText(game.getName());
gameBackgroundPreview.setBackgroundColor(game.getColor());
colorSelected = game.getColor();
for(int i = 0; i < game.getSteps().size() ; i++){
//steps.add(game.getSteps().get(i).toString());
//notifyDataSetChanged();
stepsAdapter.add(game.getSteps().get(i).toString());
}
final ListView listSteps = (ListView) findViewById(R.id.listViewSteps);
listSteps.setAdapter(stepsAdapter);
gameNameValue.setText(gameName);
addSteps.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
stepsId = steps.size();
Toast.makeText(getApplicationContext(), "addSteps : " + stepsId, Toast.LENGTH_LONG).show();
stepsAdapter.insert("newstep", stepsId);
}
});
buttonSaveGame.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String valueEditGameName = gameNameValue.getText().toString();
int valueColorBackaground = colorSelected;
String picture = "testPic";
for(int i=0; i < listSteps.getChildCount(); i++) {
LinearLayout rowLayout = (LinearLayout) listSteps.getChildAt(i);
//Log.e(TAG, ">> :) layout >>" + listSteps.getChildAt(i).getClass().getName());
EditText editRow = (EditText) rowLayout.getChildAt(0);
stepsValues.add(editRow.getText().toString());
//Log.e(TAG, ">> :) inside layout >>" + editRow.getText().toString());
}
if(valueEditGameName.trim().length() > 0 && picture.trim().length() >0 ){
Games game = new Games(valueEditGameName,valueColorBackaground,picture,stepsValues);
String goToSave = game.createJson();
Log.e(TAG, ">>Saved>>" + goToSave);
final CkdFile file = new CkdFile();
String saved = file.writeToSDFile(game.getName(), goToSave);
Toast.makeText(mContext, saved, Toast.LENGTH_LONG).show();
Intent backToMain = new Intent(mContext,MainActivity.class);
startActivity(backToMain);
} else {
Toast.makeText(mContext, "Fill all texts", Toast.LENGTH_LONG).show();
}
}
});
}
I try to add items in 2 different ways :
add item through : List steps
add item through : StepsAdapter stepsAdapter
Both give me same behaviour.
If someone has a clue to help understanding what i'm doing wrong with my implementation of ListView/ArrayAdapter.
Thanks in advance !
EDIT 1 :
After pushing some logs everywere, it understand the strange behaviour :
My adapter have only 6 slots (the limit came from the size of the listview in layout), and when my arraylist have more than 6 items, the getView select items only between 0 and 5.
I'm searching now a way to get the position in ArrayList and not the position in arrayadapter.
I faced same issue recently. Add following overrides to Adapter:
#Override
public int getViewTypeCount() {
return getCount();
}
#Override
public int getItemViewType(int position) {
return position;
}
I found a simple xml "trick" to avoid this behaviour : i set a biger height to listView.
<ListView
android:layout_width="match_parent"
android:layout_height="1000dp"
android:layout_gravity="center_horizontal"
android:id="#+id/listViewSteps"
android:layout_margin="10dp">
</ListView>
It's not really resolve but a take it ...

ListView not refreshed until user interaction

I have a ListView and a custom adapter. When the user changes the sort type from a spinner, I want to redraw the list with the new items. The problem is that the ListView is re-drawn only after I interact with it on the device (start scrolling for example).
if (update) {
mOfferList = (ArrayList<PositionSearchItem>) psr
.getPositionSearchItem();
mAdapter.clear();
mAdapter.addAll(mOfferList);
mAdapter.notifydataSetChanged()
mList.invalidate();
mList.invalidateViews();
}
I have also tried to fully reset a new instance of my adapter to the list, it is giving the same result except that the list becomes blank at first and appears on user interaction.
I'm using HoloEveryWhere maybe this is an important info.
EDIT : Here is some more code
#EFragment(R.layout.list_fragment_results)
public class ResultsListFragment extends Fragment {
#ViewById(R.id.search_results_list)
ListView mList;
#ViewById(R.id.search_result_list_spinner)
Spinner mSpinner;
MainActivity mActivity;
private String mTypedText;
private List<String> mSortType;
private ResultsListFragment mFrag;
private Bundle mOffersPageBundle;
private SearchResultsListAdapter mAdapter;
private ArrayList<PositionSearchItem> mOfferList;
#AfterViews
public void afterViews() {
mFrag = this;
mActivity = (MainActivity) getActivity();
mSortType = new ArrayList<String>();
mSortType.add(SearchResultSortType.SCORE_DESCENDING);
mOfferList = new ArrayList<PositionSearchItem>();
mOffersPageBundle = getArguments();
PositionSearchResponse psr = mOffersPageBundle.getParcelable("offers");
mTypedText = mOffersPageBundle.getString("typedText");
mOfferList = (ArrayList<PositionSearchItem>) psr.getPositionSearchItem();
mAdapter = new SearchResultsListAdapter(this,
android.R.layout.simple_list_item_1, mOfferList, mTypedText,
mSortType, mActivity.getLat(), mActivity.getLon());
mList.setAdapter(mAdapter);
this.setListeners();
}
private void setListeners() {
mSpinner.setOnItemSelectedListener(new org.holoeverywhere.widget.AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(
org.holoeverywhere.widget.AdapterView<?> parent, View view,
int pos, long id) {
Boolean update = false;
PositionSearchResponse psr = null;
switch (pos) {
case 0:
if (mSortType.get(0) != SearchResultSortType.SCORE_DESCENDING) {
mSortType.clear();
mSortType.add(SearchResultSortType.SCORE_DESCENDING);
psr = processSearch(mTypedText, mSortType, 0, mActivity);
update = true;
}
break;
case 1:
if (mSortType.get(0) != SearchResultSortType.DATE) {
mSortType.clear();
mSortType.add(SearchResultSortType.DATE);
psr = processSearch(mTypedText, mSortType, 0, mActivity);
update = true;
}
break;
default:
break;
}
if (update) {
mOffersPageBundle.putParcelable("offers", psr);
mOfferList = (ArrayList<PositionSearchItem>) psr
.getPositionSearchItem();
mAdapter.mIdMap.clear();
for (int i = 0; i < mOfferList.size(); ++i) {
mAdapter.mIdMap.put(mOfferList.get(i), i);
}
mOfferList = (ArrayList<PositionSearchItem>) psr
.getPositionSearchItem();
mAdapter.clear();
mAdapter.addAll(mOfferList);
mAdapter.notifydataSetChanged()
mList.invalidate();
mList.invalidateViews();
}
}
#Override
public void onNothingSelected(
org.holoeverywhere.widget.AdapterView<?> parent) {
// TODO Auto-generated method stub
}
});
}
}
EDIT : the getView method from custom adapter
#Override
public View getView(int pos, View convertView, ViewGroup parent) {
this.v = convertView;
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
PositionSearchItem psr = getItem(pos);
v = vi.inflate(R.layout.result_list_item, parent, false);
v.setTag(pos);
// Inject text into view
((TextView) v.findViewById(R.id.result_title_textview))
.setText(getItem(pos).getTitle());
// Disable hardware acceleration for the view (it brakes dotted lines on
// some devices)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
v.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
// reload list if scrolled to bottom
int page = 0;
if (pos > mLastViewed && pos == getCount() - 1) {
mLastViewed = pos;
ResultsListFragment frag = new ResultsListFragment_();
PositionSearchResponse newSearch = frag.processSearch(mTypedText, mSortType, mPage,
mActivity);
mPage = mPage + 12;
if(newSearch != null && newSearch.getPositionSearchItem() != null && newSearch.getPositionSearchItem().size() > 0){
for (int i = 0; i < newSearch.getPositionSearchItem().size(); ++i) {
mIdMap.put(newSearch.getPositionSearchItem().get(i), i);
}
this.addAll(newSearch.getPositionSearchItem());
}
}
return v;
}
Use
if (update) {
mAdapter.NotifyDataSetChanged();
}
this may solve your problem.
Try to set the adatpter again.Place the below code in all the places you wrote the code to update listview
mAdapter = new YourAdapter();
mList.setAdapet(mAdapter)
I hope you have created custom list view using adapter and model.
Now each time when you need to update your list view dynamically then you need to clear your previously created list view. Then after you need to fetch all new values and then update same over custom list view.
Hope it helps!

Gingerbread listview item animation issue

I've looked for this issue but nothing I've found matches my situation.
My problem occurs onAndroid 2.3.x (on 4.x it works perfectly)
I have an application with a custom list view. I initialize my listview as follows
ListAdapter mListAdapter = new ListAdapter(getApplicationContext(), this, ...);
lvSelector = (ListView) findViewById(R.id.listView1);
lvSelector.setAdapter(mListAdapter);
My ListAdapter is as follows:
public class ListAdapter extends BaseAdapter {
static class Holder {
LinearLayout layoutRoot, layoutColor;
TextView hText;
Animation anim = AnimationUtils.loadAnimation(mActivity, R.anim.anim_list_item);
public Holder() {
layoutRoot = new LinearLayout(mContext);
layoutColor = new LinearLayout(mContext);
hText = new TextView(mContext);
}
public Holder(Holder holder) {
this.layoutRoot = holder.layoutRoot;
this.layoutColor = holder.layoutColor;
this.hText = holder.hText;
}
}
int mSwap1, mSwap2;
Animation mAnimation;
public ListAdapter(Context _context, Activity _activity, FileHandler _fileHandler, String _strSchemaName, List<String> _list, List<String> _solution) {
mContext = _context;
mActivity = _activity;
mAnimation = AnimationUtils.loadAnimation(mActivity, R.anim.anim_list_item);
mAnimation.reset();
mSwap1 = mSwap2 = -1;
/* ... */
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final int fPosition = position;
View row = convertView;
Holder lHolder = null;
if (row==null) {
LayoutInflater inflater = mActivity.getLayoutInflater();
row = inflater.inflate(R.layout.layout_schema_element, parent, false);
lHolder = new Holder();
lHolder.layoutRoot = (LinearLayout) row.findViewById(R.id.elementLayoutRoot);
lHolder.layoutColor = (LinearLayout) row.findViewById(R.id.elementLayoutColor);
lHolder.hText = (TextView) row.findViewById(R.id.textViewWord);
row.setTag(lHolder);
}
else {
lHolder = (Holder)row.getTag();
}
row.setOnClickListener(null);
if (position==0 || position==mDataList.size()-1) {
lHolder.layoutColor.setBackgroundResource(R.drawable.bg_elem_fixed);
lHolder.layoutColor.setOnClickListener(null);
}
else {
lHolder.layoutColor.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
moveElement(fPosition);
}
});
}
lHolder.hText.setText(mDataList.get(position));
lHolder.layoutRoot.setBackgroundResource(0);
mHolder.set(position, lHolder);
return row;
}
}
protected void moveElement(int _element) {
if (mDataList.get(_element).equals(mSolution.get(_element)))
return;
if (mSwap1==-1)
{
System.out.println("setting swap1=" + _element);
mHolder.get(_element).layoutRoot.setBackgroundResource(R.drawable.bg_elem_selected_lite);
mSwap1 = _element;
}
else
{
if (mSwap2==-1)
{
System.out.println("setting swap2=" + _element);
mHolder.get(_element).layoutRoot.setBackgroundResource(R.drawable.bg_elem_selected_lite);
mSwap2 = _element;
}
}
if (mSwap1!=-1)
{
System.out.println("running animation on mSwap1=" + mSwap1);
mHolder.get(mSwap1).layoutRoot.clearAnimation();
mHolder.get(mSwap1).layoutRoot.startAnimation(mAnimation);
}
/***** THIS IS WHAT DOES NOT WORK *****/
if (mSwap2!=-1)
{
System.out.println("running animation on mSwap2=" + mSwap2);
mHolder.get(mSwap2).layoutRoot.clearAnimation();
mHolder.get(mSwap2).layoutRoot.startAnimation(mAnimation);
}
if (mSwap1!=-1 && mSwap2!=-1)
{
mHolder.get(mSwap1).layoutRoot.setBackgroundColor(0);
mHolder.get(mSwap2).layoutRoot.setBackgroundColor(0);
if (mSwap1==mSwap2)
{
mSwap1 = mSwap2 = -1;
return;
}
Collections.swap(mDataList, mSwap1, mSwap2);
Collections.swap(mHolder, mSwap1, mSwap2);
Collections.swap(dataObjs, mSwap1, mSwap2);
mSwap1 = mSwap2 = -1;
notifyDataSetChanged();
}
}
}
Everything works fine when I perform Collections.swap(list, mSwap1, mSwap2), elements are correctly swapped.
First animation (mSwap1) is run fine; my problem is that when second animation is run (mSwap2), it is executed on another element in screen even if mSwap2 is right (e.g.: mSwap1=1 -> second element in list is animated, mSwap2=2 -> n-1 element and n-2 element in list are animated where n is the number of visible elemnts).
I've solved my problem replacing animation calls
mHolder.get(idx).layoutRoot.clearAnimation();
mHolder.get(idx).layoutRoot.startAnimation(mAnimation);
with the following method
private void animateItem(int _index, Animation _animation) {
if (
_index<mLvSelector.getFirstVisiblePosition() // selected item is above first visible element
|| _index>mLvSelector.getLastVisiblePosition() // selected item is below last visible element
)
// element is invisible -> no animation
return;
int newIndex = _index;
if (
android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH // before android 4.0
&& mSwap2>=0 // second selection
&& mSwap1!=mSwap2 // second selection differs from first selection
)
newIndex = mLvSelector.getFirstVisiblePosition() + (mLvSelector.getLastVisiblePosition() - _index);
mHolder.get(newIndex).layoutRoot.clearAnimation();
mHolder.get(newIndex).layoutRoot.startAnimation(_animation);
}
Adding Animation argument to the method allows to differentiate animation between elements (mSwap1 and mSwap2).

Categories

Resources