Android: Simple GridView that displays text in the grids - java

I'm following the example on the android tutorial about the GridView, but instead of showing image, i want to just simple show some text using a TextView. it turns out seems to be harder than i thought. it might seems like this is totally unnecessary and it doesn't have a valid use case, but i'm trying this out to just get myself familiar with the sdk.
so my code is pretty much the same as the GridView example in http://developer.android.com/guide/tutorials/views/hello-gridview.html, but instead of using a ImageAdapter, i created a dummy adapter like following:
public class MyAdapter extends BaseAdapter {
private Context context;
private String[] texts = {"aaa", "bbb", "ccc", "ddd", "eee", "fff", "eee", "hhh", "iii"};
public MyAdapter(Context context) {
this.context = context;
}
public int getCount() {
return 9;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
public View getView(int position, View convertView, ViewGroup parent) {
TextView tv;
if (convertView == null) {
tv = new TextView(context);
tv.setLayoutParams(new GridView.LayoutParams(85, 85));
}
else {
tv = (TextView) convertView;
}
tv.setText(texts[position]);
return tv;
}
}
it all seems valid to me, but running this gives me nothing on the screen. and there's no error message. there are some selectable/clickable (invisible) blocks if i click them, but the text is obvious not shown. i wonder is my layout doesn't have the android:text causing this problem? or anything else?
any feedback will be appreciated and thanks for your help!

I am not sure what could be causing your problem. I followed the step by step instructions on the page that you linked to to set up "Hello, GridView", and used your code and was able to see the text.
The only things I changed was rather than creating a class for ImageAdapter I used your MyAdapter. In the activity HelloGridView.java onCreate I used "MyAdapter" rather than "ImageAdapter". I didn't change the layout at all.
Here is a Screenshot of what I get when running your code.

I see GridView so I'm almost assuming that this is similar to SWT?
If so you need to show the relationship between your view and the ViewGroup parent

I don't think your getItem implementation is correct. Top of my head it should be
return texts[position];

Related

need an example trying to use svg/png

This is my first post...im a beginner trying to make an app for android.
im trying to make a simple match game using a deck of playing cards. Ive been able to get open source svg and png playing cards online. They either come in individual cards images (in png or svg formats) or one image (svg or png) with every card spaced nicely with a contrast background.
With all of the research ive done, i try to utilize these svg or png files but run into either out of memory issues or very slow UX performance.
My goal is to get a gridview to show all 52 cards that swipe horizontally. and if i click on a card, and match its face...then i get a point.
heres my layout activty_play_board.xml
just have one item...a gridview:
<GridView
android:id="#+id/gridView"
android:layout_width="match_parent"
android:layout_height="540dp"
android:horizontalSpacing="2dp"
android:numColumns="4"
android:stretchMode="columnWidth"
android:verticalSpacing="2dp" />
I use this public class that i got from googles dev website. it allows me to fill in the gridview with the svg or png cards
public class ImageAdapter extends BaseAdapter {
private Context mContext;
public ImageAdapter(Context c) {
mContext = c;
}
public int getCount() {
return mThumbIds.length;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
// create a new ImageView for each item referenced by the Adapter
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
// if it's not recycled, initialize some attributes
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(200, 270));
imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
imageView.setPadding(4,4, 4, 4);
} else {
imageView = (ImageView) convertView;
}
imageView.setImageResource(mThumbIds[position]);
return imageView;
}
private Integer[] mThumbIds = {
R.drawable.two_of_clubs,
R.drawable.two_of_diamonds,
R.drawable.two_of_hearts,
R.drawable.two_of_spades,
R.drawable.three_of_clubs,
R.drawable.three_of_diamonds,
R.drawable.three_of_hearts,
R.drawable.three_of_spades,
R.drawable.four_of_clubs,
R.drawable.four_of_diamonds,
R.drawable.four_of_hearts,
R.drawable.four_of_spades,
R.drawable.five_of_clubs,
R.drawable.five_of_diamonds,
R.drawable.five_of_hearts,
R.drawable.five_of_spades,
R.drawable.six_of_diamonds,
R.drawable.six_of_hearts,
R.drawable.six_of_spades,
R.drawable.six_of_clubs,
R.drawable.seven_of_diamonds,
R.drawable.seven_of_hearts,
R.drawable.seven_of_spades,
R.drawable.seven_of_clubs,
R.drawable.eight_of_diamonds,
R.drawable.eight_of_hearts,
R.drawable.eight_of_clubs,
R.drawable.eight_of_spades,
R.drawable.nine_of_diamonds,
R.drawable.nine_of_hearts,
R.drawable.nine_of_spades,
R.drawable.nine_of_clubs,
R.drawable.ten_of_clubs,
R.drawable.ten_of_diamonds,
R.drawable.ten_of_hearts,
R.drawable.ten_of_spades,
R.drawable.jack_of_clubs,
R.drawable.jack_of_diamonds,
R.drawable.jack_of_hearts,
R.drawable.jack_of_spades,
R.drawable.queen_of_clubs,
R.drawable.queen_of_diamonds,
R.drawable.queen_of_hearts,
R.drawable.queen_of_spades,
R.drawable.king_of_spades,
R.drawable.king_of_clubs,
R.drawable.king_of_diamonds,
R.drawable.king_of_hearts,
R.drawable.ace_of_spades,
R.drawable.ace_of_diamonds,
R.drawable.ace_of_hearts,
R.drawable.ace_of_clubs
};}
heres my onCreate:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_grid_board);
Button goBackOnClick = (Button) findViewById(R.id.playBoardButton);
GridView gridview = (GridView) findViewById(R.id.gridView);
gridview.setAdapter(new ImageAdapter(this));
gridview.setOnItemClickListener(newAdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
Toast.makeText(GridBoard.this, "Spot: " + position, Toast.LENGTH_LONG).show();
}
});
I have several questions.
How do i utilize the SVG file? am i calling the one big svg file and then referencing a specific location on the file when i call it for an imageview within my gridview?
i dont know how to use the one big SVG file, so ive tried using the individual svg images. but when i do that, my device is very laggy and slow. also sometimes the app crashes due to out of memory errors. Is there a special syntax to use when trying to reference a specific card within the large SVG file? Or do i have to manually edit the big svg file and get the smaller cards?
Is there a better way to do this than using a gridview?
any other general guidance would be greatly appreciated.
Thank you!
Heres my card sources:
one big palette
https://upload.wikimedia.org/wikipedia/commons/8/81/English_pattern_playing_cards_deck.svg
individual cards and one big palette
http://byronknoll.blogspot.com/2011/03/vector-playing-cards.html

Scroll Android ListView to top without scroll animation [duplicate]

I have a ListActivity that implements onListItemClick() and calls a doSomething() function of the class. The latter contains l.setSelection(position) where l is the ListView object.
Now there is a onClickListener() listening for a button click that perfoms some actions and that too calls doSomething().
In the first case, the selected item get positioned appropriately, but in the latter, nothing happens.
Any clues about this strange behaviour and how I might make it work?
maybe you need to use function:
ListView.setItemChecked(int position, boolean checked);
use requestFocusFromTouch() before calling setSelection() method
I know this is an old question but I just had a similar problem that I solved in this way:
mListView.clearFocus();
mListView.post(new Runnable() {
#Override
public void run() {
mListView.setSelection(index);
}
});
You might need to wrap setSelection() in a posted Runnable (reference).
setSelection() does not necessarily have visual impact. The selection bar only appears if you use the D-pad/trackball to navigate the list. If you tap on the screen to click something, the selection bar appears briefly and vanishes.
Hence, setSelection() will only have a visual impact if the activity is not in touch mode (i.e., the last thing the user did was use the D-pad/trackball).
I am not 100% certain this explains your phenomenon given the description you provided, but I figured it is worth a shot...
If you use an Adapter for your ListView add this code to your adapter:
public class MyAdapter extends
ArrayAdapter<MyClass> {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflator = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
rowView = inflator.inflate(R.layout.my_adapter, null);
} else {
rowView = (View) convertView;
}
//...
// set selected item
LinearLayout ActiveItem = (LinearLayout) rowView;
if (position == selectedItem)
{
ActiveItem
.setBackgroundResource(R.drawable.background_dark_blue);
// for focus on it
int top = (ActiveItem == null) ? 0 : ActiveItem.getTop();
((ListView) parent).setSelectionFromTop(position, top);
}
else
{
ActiveItem
.setBackgroundResource(R.drawable.border02);
}
}
private int selectedItem;
public void setSelectedItem(int position) {
selectedItem = position;
}
}
In your Activity:
myAdapter.setSelectedItem(1);
For me calling
listView.notifyDataSetChanged();
listView.requestFocusFromTouch();
and then
listView.setSelection(position);
solved the issue.
if you do that in a runnable it works without calling requestFocusFromTouch(), but the old position of the ListView is showen for a sekound.
I have an very large Request with Webcontent. When I used the code in onCreateView the Listview wasnt even finished loading.
I put it in onPostExecute of my AsyncTask.
//Get last position in listview
if (listView != null && scrollPosition != 0) {
listView.clearFocus();
listView.requestFocusFromTouch();
listView.post(new Runnable() {
#Override
public void run() {
listView.setItemChecked(scrollPosition, true);
listView.setSelection(scrollPosition);
}
});
}
Dont forget to set the item checked in on Click ;)
Maybe you should use the smoothScrollToPosition(int position) method of ListView
You can try 2 ways like these:
Solution A:
mListView.post(new Runnable() {
#Override
public void run() {
if (null != mListView) {
mListView.clearFocus();
mListView.requestFocusFromTouch();
mListView.setSelection(0);
}
}
});
In some complicated situation, this solution will bring some new problems in Android 8.x. Besides it may cause unexpected onFocusChange().
Solution B:
Define a custom view extends ListView. Override method handleDataChanged().Then setSelection(0). In CustomListView:
#Override
protected void handleDataChanged() {
super.handleDataChanged();
if (null != mHandleDataChangedListener){
mHandleDataChangedListener.onChanged();
}
}
HandleDataChangedListener mHandleDataChangedListener;
public void setHandleDataChangedListener(HandleDataChangedListener handleDataChangedListener) {
this.mHandleDataChangedListener = handleDataChangedListener;
}
public interface HandleDataChangedListener{
void onChanged();
}
In activity:
mListView.setHandleDataChangedListener(new CustomListView.HandleDataChangedListener() {
#Override
public void onChanged() {
mListView.setHandleDataChangedListener(null);
mListView.setSelection(0);
}
});
mAdapter.notifyDataSetChanged();
Ok, That's it.
In my case smoothScrollToPosition(int position) worked, can you also tell me how to set that scrolled position into center of the list. It appeared at the bottom of visible items.
For me it helped to set
ListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); or ListView.CHOICE_MODE_MULTIPLE
then
ListView.setSelection(position) or ListView.setItemChecked(position, true);
works fine
Found a solution in my case. I am not using a Runnable since my class is extending ListFragment. What I had to do is make my index a final;
final index = 5;
mListView.setSelection(index);
I found that sometimes setSelection will not work because I set attribute "android:height" of listView to "wrap_content".
And the times my App won't work is that when listView become scrollable from non-scrollable.
For example, if my app is "File Browser App". When my list is less than, let's say 6, then it's non-scrollable. Now I return to the parent directory, and it has 11 objects, and I want to set selection to some position, and it won't work here.
to\from | Scrollable | non-Scrollable
Scrollable | O | O( of course )
non-Scrollable | X | O( of course )
I don't want to use post(Runnable), because there will has delay.
==================================
Answer:
You can try to set "android:height" to "match_parent"
God, it spends three days.
When use post to setSelection(), the ListView will see first , then scroll to the position , thank to "魏經軒", then layout actually will effect the setSelection(), because setSelection() call the setSelectionFromTop(int position, int y), there is another way to solve it.
listView.setAdapter(listView.getAdapter());
listView.setSelection(123);
For me the solution to this problem was:
listView.clearChoices();
Simply try this code
listView.setAdapter(adapter);
listView.setSelection(position);
adapter.notifyDataSetChanged();

ArrayList in an adapter?

So guys, i'll try to explain a bit of this part of my project i wanted to do so you guys can better understand my problem. I'm trying no develop an app to a bus station, to show the schedule. And to show it, i was using a ListView, a costum one. And it works if i only pass Strings to the adapter. But in order to do only in an Array of Strings would take a lot of memory cause i would have to build a lot of arrays. Because, try to imagine, i have the schedule of the bus leaving a certain place, but then, depending in what destination the user selects i have to exclude some of items from the array because not all bus's end at the same place, some won't go to the destination selected, so i have to take those out. So if i was going to do that in just multiple arrays e would have to build dozens of array to every single possibilitie of departure and destination. So i thought in doing it with an arraylist, so would be easier to remove the items i don't want.
Now the problem is that i can't send to the adapter of the costum listview and arraylist, like this:
ArrayList<String> horarios= new ArrayList<String>();
ListAdapter horarioAdapter = new costum_adapter(this, horarios);
ListView horarioListView = (ListView) findViewById(R.id.horario_listView);
horarioListView.setAdapter(horarioAdapter);
It gives me error in the second line, because i can't put "horarios" in adapter because its an arraylist. How can i solve this ? Hope you guys understood what i was trying to do.
Sorry for the long description.
Here is my costum_adapter, doesn't have much, only 2 textView, i was just trying this to work, and then add a few things.
class costum_adapter extends ArrayAdapter<String>{
public costum_adapter(Context context, String[] horarios) {
super(context, R.layout.costum_listview ,horarios);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater horarioInflater = LayoutInflater.from(getContext());
View costumView = horarioInflater.inflate(R.layout.costum_listview, parent, false);
String singleHorario = getItem(position);
TextView hora = (TextView) costumView.findViewById(R.id.Hora);
TextView nota = (TextView) costumView.findViewById(R.id.Nota);
hora.setText(singleHorario);
nota.setText(" ");
return costumView;
}
}
Fix arguments in adapter constructor
public costum_adapter(Context context, ArrayList<String> horarios) {
super(context, R.layout.costum_listview ,horarios);
}

Refresh ListView with ArrayAdapter

I'm parsing a web service to display in a listView backed by my own subclass of ArrayAdapter. The data is static ArrayList<Wait> in Application.java. You'll see it referenced by App.getWaits().
I use a simple refresh method for when there's new data. I've confirmed that it's being updated but it only renders if I navigate away and then return to the view.
In the past I've been able to refresh the listView by calling notifyDataSetChanged() on the adapter but right now none of these have worked for me. Thanks for taking a look... any ideas!?
//1 This is how I'd normally update the listView dynamically, but not tonight.
adapter.notifyDataSetChanged();
//2 It's the same thing really, so no good.
((WaitAdapter) list.getAdapter()).notifyDataSetChanged();
//3 Saw this as the answer to a similar question, doesn't work.
adapter.getWaits().clear();
adapter.getWaits().addAll(App.getWaits());
adapter.notifyDataSetChanged();
//4 Called in onCreate but tried a 2nd time in refresh() to manually reset adapter, doesn't work.
adapter = new WaitAdapter(getHost().getApplicationContext(), App.getWaits());
list.setAdapter(adapter);
//5 Kinda the same thing, new adapter, reset adapter... also no good.
WaitAdapter adapter = new WaitAdapter(getHost().getApplicationContext(), App.getWaits());
list.setAdapter(adapter);
//6 I read ArrayAdapter keeps its own reference to initial data object but this fails too.
adapter = null;
adapter = new WaitAdapter(getHost().getApplicationContext(), App.getWaits());
list.setAdapter(adapter);
*Update to share my WaitAdapter.java.
public class WaitAdapter extends ArrayAdapter<Wait> {
private LayoutInflater inflater;
private ArrayList<Wait> waits;
public WaitAdapter(Context context, ArrayList<Wait> data) {
super(context, R.layout.list_item_wait, data);
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
waits = data;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.list_item_wait, parent, false);
holder = new ViewHolder();
holder.checkpointName = (TextView) convertView.findViewById(R.id.checkpointName);
holder.delayAmount = (TextView) convertView.findViewById(R.id.delayAmount);
holder.timeReported = (TextView) convertView.findViewById(R.id.timeReported);
holder.dateReported = (TextView) convertView.findViewById(R.id.dateReported);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Wait wait = waits.get(position);
holder.checkpointName.setText(wait.getName());
holder.delayAmount.setText(wait.getDelayInMinutes());
holder.timeReported.setText(wait.getTimeLabel());
holder.dateReported.setText(wait.getDateLabel());
return convertView;
}
#Override
public boolean isEnabled(int position) {
return false;
}
static class ViewHolder {
TextView checkpointName;
TextView delayAmount;
TextView timeReported;
TextView dateReported;
}
}
12/14/14 Update: General implementation background.
At launch the App class starts WaitAsyncTask, which parses remote XML to fill its ArrayList waits. I'll access these waits in a few places so this way I keep them global.
WaitFragment, working with WaitAdapter, displays waits in a ListView and listens for changes to waits. User's can post waits to the web service via an AlertDialog. A successful response executes WaitAsyncTask again, updating the waits object, triggering a WaitFragment refresh().
Console logs and the web service confirm this flow of control and that waits gets updated. If I leave WaitFragment then return, it shows the updated waits. The code posted with comments #1-6 are what I've tried inside of the refresh() to update the ListView.
I use this general approach with other data and fragments in this app and their UIs refresh as intended, but none are listViews. I'm not sure I could post more source without redacting most of it but I'll share my findings once I get it working. I haven't had trouble with ListView before, but it'll be something embarrassing for sure. Thanks to everyone who took a little time :)
Just create a method in Adapter class to update/refresh the listview as follows,
public class WaitAdapter extends ArrayAdapter<Wait> {
private LayoutInflater inflater;
private ArrayList<Wait> waits;
public WaitAdapter(Context context, ArrayList<Wait> data) {
super(context, R.layout.list_item_wait, data);
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
waits = data;
}
/**
* Update content
*/
public void updateListContent(ArrayList<Wait> data)
{
waits = data;
notifyDataSetChanged();
}
}
In your acivity class, just call this adapter method to update the content. Refer the below code
Note:Dont clear the array content of the adapter.
//Dont clear the arraylist of adapter
adapter.updateListContent(App.getWaits());
This may help you.

Listview with custom adapter containing CheckBoxes

I have a ListView which uses a custom adapter as shown:
private class CBAdapter extends BaseAdapter implements OnCheckedChangeListener{
Context context;
public String[] englishNames;
LayoutInflater inflater;
CheckBox[] checkBoxArray;
LinearLayout[] viewArray;
private boolean[] checked;
public CBAdapter(Context con, String[] engNames){
context=con;
englishNames=engNames;
inflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
checked= new boolean[englishNames.length];
for(int i=0; i<checked.length; i++){
checked[i]=false;
//Toast.makeText(con, checked.toString(),Toast.LENGTH_SHORT).show();
}
checkBoxArray = new CheckBox[checked.length];
viewArray = new LinearLayout[checked.length];
}
public int getCount() {
return englishNames.length;
}
public Object getItem(int position) {
// TODO Auto-generated method stub
return null;
}
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
public View getView(int position, View convertView, ViewGroup parent) {
if(viewArray[position] == null){
viewArray[position]=(LinearLayout)inflater.inflate(R.layout.record_view_start,null);
TextView tv=(TextView)viewArray[position].findViewById(R.id.engName);
tv.setText(englishNames[position]);
checkBoxArray[position]=(CheckBox)viewArray[position].findViewById(R.id.checkBox1);
}
checkBoxArray[position].setChecked(checked[position]);
checkBoxArray[position].setOnCheckedChangeListener(this);
return viewArray[position];
}
public void checkAll(boolean areChecked){
for(int i=0; i<checked.length; i++){
checked[i]=areChecked;
if(checkBoxArray[i] != null)
checkBoxArray[i].setChecked(areChecked);
}
notifyDataSetChanged();
}
public void onCheckedChanged(CompoundButton cb, boolean isChecked) {
for(int i=0; i<checked.length; i++){
if(cb == checkBoxArray[i])
checked[i]=isChecked;
}
}
public boolean itemIsChecked(int i){
return checked[i];
}
}
The layouts are fairly simple so I won't post them unless anyone thinks they are relevant.
The problem is that some of the CheckBoxes are not responding. It seems to be the ones that are visible when the layout is first displayed. Any that you have to scroll down to work as expected.
Any pointers appreciated.
Your code from the answer works but is inefficient(you can actually see this, just scroll the ListView and check the Logcat to see the garbage collector doing it's work). An improved getView method which will recycle views is the one below:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LinearLayout view = (LinearLayout) convertView;
if (view == null) {
view = (LinearLayout) inflater.inflate(R.layout.record_view_start, parent, false);
}
TextView tv = (TextView) view.findViewById(R.id.engName);
tv.setText(getItem(position));
CheckBox cBox = (CheckBox) view.findViewById(R.id.checkBox1);
cBox.setTag(Integer.valueOf(position)); // set the tag so we can identify the correct row in the listener
cBox.setChecked(mChecked[position]); // set the status as we stored it
cBox.setOnCheckedChangeListener(mListener); // set the listener
return view;
}
OnCheckedChangeListener mListener = new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mChecked[(Integer)buttonView.getTag()] = isChecked; // get the tag so we know the row and store the status
}
};
Regarding your code from your question, at first I thought it was wrong because of the way you setup the rows but I don't see why the adapter will have that behavior as you detached the row view from the list. Also, I even tested the code and it works quite well regarding CheckBoxes(but with very poor memory handling). Maybe you're doing something else that makes the adapter to not work?
Let me first say that you have thrown away one of the main benefits of using an adapter: Reusable views. Holding a hard reference to each created View holds a high risk of hitting the memory ceiling. You should be reusing convertView when it is non-null, and creating your view when convertView is null. There are many tutorials around which show you how to do this.
Views used in an adapter typically have an OnClickListener attached to them by the parent View so that you can set a OnItemClickListener on the ListView. This will supersede any touch listeners on the individual views. Try setting android:clickable="true" on the CheckBox in XML.
This may not be the most elegant or efficient solution but it works for my situation. For some reason attempting to reuse the views either from an array of views or using convertView makes every thing go wobbley and the CheckBoxes fail to respond.
The only thing that worked was creating a new View everytime getView() is called.
public View getView(final int position, View convertView, ViewGroup parent) {
LinearLayout view;
view=(LinearLayout)inflater.inflate(R.layout.record_view_start,null);
TextView tv=(TextView)view.findViewById(R.id.engName);
tv.setText(englishNames[position]);
CheckBox cBox=(CheckBox)view.findViewById(R.id.checkBox1);
cBox.setChecked(checked[position]);
cBox.setOnCheckedChangeListener(new OnCheckedChangeListener(){
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
checked[position]=isChecked;
}
});
return view;
}
Finding this solution was also hampered by the fact that I was calling a separately defined onCheckedChangedListener, that then identified which CheckBox by id, rather than having a new listener for each CheckBox.
As yet I haven't marked this as the correct answer as I'm hoping that others may have some input regarding the rather wasteful rebuilding the view every time.

Categories

Resources