I have an activity which is populated by a GridView containing a 3x3 matrix
of LinearLayouts (which in turn have ImageViews). When I try to get a position
of view after some the GridView is completely loaded, I get proper results.
However, when I try to fetch the view at a given position from onResume(),
it gives a NullPointerException. Why does this happen? Isn't the GridView
initialized by the time onResume() is called?
Are there any workaround for this?
The part where I am trying to fetch the view of a given position -
private void systemsMove(String userBlock) {
int position, row, column;
String systemsBlock = (userBlock.equalsIgnoreCase("Zero") ? "One" : "Zero");
for (position = 0, row = 0, column = 0;
(mModel.array[row][column] == 0
|| mModel.array[row][column] == 1) && position < 9;
position++) {
row = position / MAX_COLUMN;
column = position % MAX_COLUMN;
}
if (position == 9) finish();
mModel.changeBlock(systemsBlock, row, column);
View view = getViewByPosition(position);
if (view == null)
Log.i(TAG, "The view is NULL!!!");
ImageView imageView = (ImageView) view.findViewById(R.id.imageview);
if (systemsBlock.equalsIgnoreCase("Zero"))
imageView.setImageResource(R.drawable.one);
else if (systemsBlock.equalsIgnoreCase("One"))
imageView.setImageResource(R.drawable.zero);
}
Code that works from OnItemClickListener -
gridView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
ImageView imageView = (ImageView) view.findViewById(R.id.imageview);
String systemBlock = "";
if (userBlock.equalsIgnoreCase("One")) {
imageView.setImageResource(R.drawable.one);
systemBlock = "Zero";
} else if (userBlock.equalsIgnoreCase("Zero")) {
imageView.setImageResource(R.drawable.zero);
systemBlock = "One";
}
int row = position / MAX_COLUMN;
int column = position % MAX_COLUMN;
mModel.changeBlock(userBlock, row, column);
if (mModel.checkForWin()) {
mModel.deInitialize();
finish();
}
systemsMove(systemBlock);
if (mModel.checkForWin()) {
mModel.deInitialize();
finish();
}
}
});
Method for getting View for a position in a GridView from this SO question -
public View getViewByPosition(int position) {
int firstPosition = gridView.getFirstVisiblePosition();
int lastPosition = gridView.getLastVisiblePosition();
if ((position < firstPosition) || (position > lastPosition))
return null;
return gridView.getChildAt(position - firstPosition);
}
And this is how I call it from onResume() -
protected void onResume() {
super.onResume();
String userBlock = getIntent().getStringExtra("BlockSelected");
if (userBlock.equalsIgnoreCase("Zero")) systemsMove(userBlock);
}
GridView will populate itself from the adapter during the first layout pass. This means it won't have children in onCreate(). The easiest way to wait for the first layout pass is to use a ViewTreeObserver (see View.getViewTreeObserver()) to register a global layout listener. The first time the listener is invoked, the grid should contain children.
Note that if you want to change the background of one of the children of a GridView you really should do it from the adapter. Since views are recycled the background may appear to "move" to another view later on
Related
Hello am implementing a listView which contains the An Imageview, when the user clicks on the image the text changes to let say 1, and the colour changes to red and vice-versa but when the user scroll down or up it goes back to the original Text and original imageview colour which loaded first with the listview, I tried using
notifyDataSetChanged();
but this does not work
code for OnClicks:
holder.ImageLike.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
resultp = data.get(position);
if(holder.ImageLike.getDrawable().getConstantState().equals(context.getResources().getDrawable(R.drawable.ic_favorite_white_24dp).getConstantState())){
resultp = data.get(position);
new Like().execute();
String number = holder.likesCountTV.getText().toString();
int num = Integer.parseInt(number);
int nene = num + 1;
String str = String.valueOf(nene);
holder.likesCountTV.setText(str);
holder.ImageLike.setImageResource(R.drawable.ic__red_24dp);
}
else{
new UnLike().execute();
String number = holder.likesCountTV.getText().toString();
int num = Integer.parseInt(number);
int nene = num - 1;
String str = String.valueOf(nene);
holder.likesCountTV.setText(str);
holder.ImageLike.setImageResource(R.drawable.ic_favorite_white_24dp);
}
}
ListView recycles the items in the list. Only the visible elements are retained.
You need to load the items in your Adapter when "getView(...)" is called
Something like:
#Override
public View getView(int position, View view, ViewGroup parent) {
if (view == null) {
view = inflater.inflate(listItemLayout, null);
}
populateView(getItem(position), view);
return view;
}
Hope this helps.
Besides updating the TextView, update also your dataset inside the onClick method.
So instead of only: holder.likesCountTV.setText(str); you also do:
holder.likesCountTV.setText(str);
resulp.something(str);
You don't and you shouldn't call notifyDataSetChanged() unless your whole dataset is changed.
I want to use imageview like a radio button in custom listview so i used a for loop and getChildAt(position) but it doesn't work well when i scroll down it doesn't change imageview image src.
This is my code :
lvChooseReader.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, final int position, long id) {
for (int i = 0; lvChooseReader.getLastVisiblePosition() - lvChooseReader.getFirstVisiblePosition() > i; i++) {
View childView = lvChooseReader.getChildAt(i);
ImageView myImage= (ImageView) childView.findViewById(R.id.ivTest);
if (i == position) {
rr.setImageDrawable(getResources().getDrawable(R.drawable.check_mark_default));
}else {
rr.setImageDrawable(getResources().getDrawable(R.drawable.check_mark_selected));
}
}
lvChooseReader.invalidateViews();
}
});
or could any one give me a link to make radio button on item click not the radio button onclick...
lvChooseReader.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, final int position, long id) {
for (int i = lvChooseReader.getFirstVisiblePosition(); lvChooseReader.getLastVisiblePosition() > i; i++) {
View childView = lvChooseReader.getChildAt(i);
ImageView myImage= (ImageView) childView.findViewById(R.id.ivTest);
if (i == position) {
rr.setImageDrawable(getResources().getDrawable(R.drawable.check_mark_default));
}else {
rr.setImageDrawable(getResources().getDrawable(R.drawable.check_mark_selected));
}
}
lvChooseReader.invalidateViews();
The deference is:
In this code the value of i will be between first visible item to last visible item and in your case value of i was between on and total visible items.
For example:
If item 2-3-4-5 are visible i should be 1-2-3-4(position of these items in listview).
and in your case it was 0-1-2-3
i solved my problem by using static values :
i add this static variable in the dialogfragment which contain my listview
static int selectedReaderId;
and for onItemClick in listview i add this :
ChooseReader reader = chooseReaderArrayList.get(position);
selectedReaderId=reader.getReaderID();
lvChooseReader.invalidateViews();
then in the custom adapter inside my getview i add this
final ChooseReader chooseReader = readers.get(position);
if (chooseReader.getReaderID()==ChooseReaderDialog.selectedReaderId){
holder.ivTest.setImageDrawable(context.getResources().getDrawable(R.drawable.check_mark_default));
}else { holder.ivTest.setImageDrawable(context.getResources().getDrawable(R.drawable.check_mark_selected));
}
I wish to manually rearrange the order of a ListView. The way I (want to) achieve this is by tapping on the item to be moved, setting the background of that item to a different colour, storing its position (oldPosition) and then tapping on the item which it is to appear below and finally resetting the background of the original item location.
The code I use to do this is:-
List<String> catarray; // string array declared in main activity
ArrayAdapter<String> catadapter; // adapter for Spinner declared in main activity
ListView cats; // listview declared in list activity
ArrayAdapter<String> adapter; // adapter for listview declared in list activity
int oldPosition = -1;
cats.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (oldPosition < 0) {
oldPosition = position;
try {
dr = parent.getChildAt(position).getBackground();
parent.getChildAt(position).setBackgroundColor(Color.argb(255, 0, 153, 204));
}
catch (Exception e) {
}
}
else {
String item = PhotoActivity.catarray.remove(oldPosition);
PhotoActivity.catarray.add(position,item);
try {
parent.getChildAt(oldPosition).setBackground(dr);
}
catch (Exception e) {
}
oldPosition = -1;
changed = true;
PhotoActivity.catadapter.notifyDataSetChanged();
adapter.notifyDataSetChanged();
}
}
});
The problem that I have is that if the list is larger than the displayed view then both the item that I tap on gets the background changed but so does an additional item that is below the visible range.
So, for example, if the full list is, say, 12 items long of which the first 8 are being displayed, if I tap on the second item then both that item but also item 11 (ie the second item plus one below the visible range) is highlighted.
Why does this happen?
How can I either stop it or, if I can't do that, reset the incorrectly highlighted item given that it is not visible and therefore not accessible via parent.getChildAt ...
The only answer I've found is to add a getView method for the adapter, call super.getView and then change the background colour there.
Also, you need to call view.setBackgroundColor in the OnItemClickListener event to make sure that the background is changed at the point of clicking.
So, my code ends up as being:-
cats.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (oldPosition < 0) {
oldPosition = position;
try {
view.setBackgroundColor(Color.argb(255, 0, 153, 204));
}
catch (Exception e) {
}
}
else {
String item = PhotoActivity.catarray.remove(oldPosition);
PhotoActivity.catarray.add(position,item);
oldPosition = -1;
changed = true;
PhotoActivity.catadapter.notifyDataSetChanged();
adapter.notifyDataSetChanged();
}
}
});
adapter = new ArrayAdapter<String>(c, android.R.layout.simple_list_item_1, PhotoActivity.catarray) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView textView = (TextView) super.getView(position, convertView, parent);
if (oldPosition> -1 && oldPosition==position) {
textView.setBackgroundColor(Color.argb(255, 0, 153, 204));
}
else {
textView.setBackgroundColor(Color.argb(0, 0, 0, 0));
}
return textView;
}
};
cats.setAdapter(adapter);
My thanks to Cameron Saul at SO answer here for pointing me in the right direction.
I have a ListView. Inside the adapter, when I do the following (code below), the firstVisible is only equal to position when the list first displays. During scrolling, when the view is visibly at the top, the adapter is still not capturing it as being at the top.
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
...
int firstVisible = ((ListView) parent).getFirstVisiblePosition();
if (firstVisible == position) {/
//print some stuff and do work
} else {
//print some other stuff and do other work
}
}
return rowView;
}
So my question is, how do I track when a specific item is at the top of the ListView during scrolling? And I want to do that from within the adapter.
EDIT
When the view in question reaches the top: first I want to set it to GONE so that it is not visible. In fact if I can only do that, that would be great. Then I want to grab its data for some work.
ok I think I've got a solution:
public class MyActivity extends Activity {
ListView listView;
ArrayAdapter<String> adapter;
int firstVisible;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
listView = (ListView) findViewById(R.id.listView);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = super.getView(position, convertView, parent);
processView(v, getItem(position), true);
return v;
}
};
for (int i = 0; i < 30; i++) {
adapter.add("Value " + String.valueOf(i + 1));
}
listView.setAdapter(adapter);
firstVisible = listView.getFirstVisiblePosition();
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem != firstVisible) {
onFirstVisibleChanged(firstVisible, firstVisibleItem);
firstVisible = firstVisibleItem;
}
}
});
}
private void processView(View v, String value, boolean fromGetView) {
//if from get view we don't want to set yet wait till it comes from scrolling
if (value.contains("4") && !fromGetView) {
v.setBackgroundResource(android.R.color.holo_orange_light);
} else {
v.setBackgroundResource(android.R.color.transparent);
}
}
private void onFirstVisibleChanged(int oldIndex, int newIndex) {
View v = getViewForPosition(newIndex);
String val = adapter.getItem(newIndex);
processView(v, val, false);
}
/**
* #param position
* position of item in list
* #return null if position outside bounds
*/
public View getViewForPosition(int position) {
int firstPosition = listView.getFirstVisiblePosition() - listView.getHeaderViewsCount();
int wantedChild = position - firstPosition;
// Say, first visible position is 8, you want position 10, wantedChild will now be 2
// So that means your view is child #2 in the ViewGroup:
if (wantedChild < 0 || wantedChild >= listView.getChildCount()) {
return null;
}
return listView.getChildAt(wantedChild);
}
}
the best way for me to explain what to do was just to try it myself.
what this does is make a list of strings "Value n" up to 30
if the string contains the number 4 when scrolling we will change the background color to orange. If you want the view to be removed we should be able to do that like this:
private void processView(View v, String value, boolean fromGetView) {
//if from get view we don't want to set yet wait till it comes from scrolling
if (value.contains("4") && !fromGetView) {
adapter.remove(value);
} else {
//no action needed
}
}
however this could be very confusing to the user as the moment Value 4 gets to the top it is removed and value 5 takes it's place.
getView is called before the view is visible, so when you scroll down, most of the time position will be firstVisible - 1 and when you scroll up firstVisible + totalVisibleItems.
Instead you should bind a scroll listener to your ListView
listView.setOnScrollListener(new OnScrollListener() {
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// Here you can change the model of the firstView and invalidate it for her to be redrawn
}
});
I've created a ListView that in each row has a button with UP and DOWN arrow. Pressing these buttons makes the row to be shifted one position up or down.
I've achieved it by implementing OnClickListener for both buttons in a the override method getView. It works as it should however I fill bad with that cuz it seems to be highly memory consuming and lots of code is doubled.
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View rowView = inflater.inflate(R.layout.row_layout, parent, false);
TextView textView = (TextView) rowView.findViewById(R.id.label);
CheckBox checkBox = (CheckBox) rowView.findViewById(R.id.checkbox);
checkBoxes.add(position, checkBox);
String address = this.getItem(position).getAddress();
String tokenizedAddress = tokenizeAddress(address);
textView.setText(tokenizedAddress);
ImageButton buttonUp = (ImageButton)rowView.findViewById(R.id.button_up);
ImageButton buttonDown = (ImageButton)rowView.findViewById(R.id.button_down);
buttonUp.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
ListAdapter adapter = ListAdapter.this;
if(position != 0 ){
GameTask current = adapter.getItem(position);
ArrayList<GameTask> list = new ArrayList<GameTask>();
for( int i = 0; i < adapter.getCount(); i++ )
list.add(adapter.getItem(i));
list.remove(position);
list.add(position-1, current);
adapter.clear();
for( int i = 0; i < list.size(); i++ ){
adapter.add(list.get(i));
}
adapter.notifyDataSetChanged();
}
}
});
buttonDown.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
ListAdapter adapter = ListAdapter.this;
if(position != adapter.getCount()-1 ){
GameTask current = adapter.getItem(position);
ArrayList<GameTask> list = new ArrayList<GameTask>();
for( int i = 0; i < adapter.getCount(); i++ )
list.add(adapter.getItem(i));
list.remove(position);
list.add(position+1, current);
adapter.clear();
for( int i = 0; i < list.size(); i++ ){
adapter.add(list.get(i));
}
adapter.notifyDataSetChanged();
}
}
});
return rowView;
}
Both listeners do almost the same, the only difference is the condition and the value of shifting +1/-1. I was wondering about creating the inner class implementing OnClickListener in the extended ArrayAdapter class however, I have no idea, how I could then pass the position of the row clicked to this inner class.
Instead of adding and removing elements from your ArrayList, you can better implement Collections.swap(List list, int firstElementIndex, int secondElementIndex) it would be much easier as you don't have to iterate through the whole Collection. A simple example for the same can be found here.
You could make a method that would be used by both buttonUp and buttonDown. This method could take as a parameter the type of action that was pressed (UP/DOWN), and the position of item in ListView, and then call this method in both of your click listener passing the appropriate action.
Example:
// 2 new constants
private static final int UP = 0;
private static final int DOWN = 1;
// Based on "type", increment or decrement the position.
private void changeRow(int type, int position){
if(type==UP){
position=position-1;
}else if(type==DOWN){
position=position+1;
}
// ........
// Then in your "for" cicle you specify:
list.add(position, current);
// ........
}
Then in the onClick() method of buttonUp you specify:
changeRow(UP, position);
and for buttonDown:
changeRow(DOWN, position);
This can be easily achieved
The way to do this is to store the data to be displayed in an List
Then when the user clicks the up or down arrow
Swap the references of data items which are shifting position using Collections.swap(List, int, int)
Then call notifyDataSetChanged on the adapter