display a listview with different formatted entries - android

I want to build a list view which displays "awards" the player has received in the game.
There are many examples out there of how to display a listview with text + icon.
However, in my case I need to vary the icon.
In the case the award is given fro a one-time achievement. In that case I just want the text and the "Trophy" to appear.
In other cases it is awarded for doing something a specified number of times.
In that case I need to include a progress bar.
How can I do the varying layouts within one list view?
Update:
Thanks to Leyths it is working (sort of).
Thing is that the ProgressBar is not showing - just its spinner.
This is the row:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:id="#+id/tvwShortMessage"
android:typeface="sans"
android:textSize="14sp"
android:textStyle="italic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView android:id="#+id/tvwLongMessage"
android:typeface="sans"
android:textSize="14sp"
android:textStyle="italic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/tvwShortMessage"/>
<ProgressBar
android:id="#+id/pgbAwardProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="#android:style/Widget.ProgressBar.Small"
android:layout_marginRight="5dp"
android:layout_below="#id/tvwLongMessage"/>
</RelativeLayout>
Code:
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)AwardsActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.awards_row, parent, false);
}
AwardItem award = awards.get(position);
ProgressBar pb = (ProgressBar) v.findViewById(R.id.pgbAwardProgress);
if(award.award_type == PROGRESSIVE) {
pb.setVisibility(View.VISIBLE);
pb.setMax(award.requirement);
pb.setProgress(award.current_amount);
} else {
pb.setVisibility(View.GONE);
}
((TextView) v.findViewById(R.id.tvwShortMessage)).
setText(MessageBuilder.buildMessage(AwardsActivity.this, award.award_text,
Integer.valueOf(award.requirement).toString()));
return v;
}
I actually want the opposite - the bar without the spinner.
Is this problem due to the special way this is being displayed or am i doing something wrong with the progress bar?
Resulting screen:

One way of doing this would be to create a class which extends a subclass of BaseAdapter, for example ArrayAdapter, and in your getView() method either inflate one of two different views (with an image and text in your case or text and a progressbar), or inflate one view and then set the visibility of the different view elements using setVisibility() to create the desired final layout.
For example:
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)YourActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.your_layout, parent, false);
}
ListObject o = items.get(position);
if(o.someCondition) {
v.findViewById(R.id.view_element).setVisibility(View.GONE);
} else {
v.findViewById(R.id.some_other_view).setVisibility(View.GONE);
}
}

Related

Android custom ArrayAdapter rendering Objects in list, not values

I'm trying to bind a custom layout (a LinearLayout containing two TextViews) to a Spinner. I subclassed ArrayAdapter (mostly) correctly. Selecting an item in the Spinner calls getView() correctly, setting the LinearLayout's TextViews' values correctly. The problem is the initial display of the items in the Spinner (when clicking on the Spinner) just shows Objects; not the TextViews they should be displaying. Only AFTER clicking on one of the Objects does the Spinner correctly set the TextViews using my custom adapter's getView() method. Here's the custom adapter class:
public class BusRouteAdapter extends ArrayAdapter<BusRoute> {
...
public BusRouteAdapter(Context context, int resource, int textViewId, ArrayList<BusRoute> routes) {
super(context, resource, textViewId, routes);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
BusRoute route = getItem(position);
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.bus_route, parent, false);
}
TextView tvBusRoute = (TextView) convertView.findViewById(R.id.tvBusRoute);
TextView tvBusRouteNumber = (TextView) convertView.findViewById(R.id.tvBusRouteNumber);
tvBusRoute.setText(route.routeName);
tvBusRoute.setTag(route.route);
tvBusRouteNumber.setText(route.route);
if (!route.routeColor.equals("")) {
tvBusRouteNumber.setBackgroundColor(Color.parseColor(route.routeColor));
}
return convertView;
}
}
Here's the layout that is to be used for each Spinner list item (bus_route.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="1">
<TextView
android:id="#+id/tvBusRouteNumber"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.25"
android:padding="8dp" />
<TextView
android:id="#+id/tvBusRoute"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.75"
android:padding="8dp" />
</LinearLayout>
And in my Activity, I'm setting the adapter to a properly-populated list of BusRoute objects:
busRouteAdapter = new BusRouteAdapter(getBaseContext(), R.layout.bus_route, R.id.tvBusRoute, arrayOfBusRoutes);
routesSpinner.setAdapter(busRouteAdapter);
It does seem strange that I need to pass the Layout id (R.layout.bus_route) AND one of the TextViews contained in that Layout (R.id.tvBusRoute).
Here's what is rendered when clicking on the Spinner:
But if I click one of the Objects, getView() is called, and the selected Layout and TextViews are rendered properly (apparently I selected "GMU - Pentagon"):
What am I missing to get the Spinner's popup list to display ALL my bus route items rendered correctly?
I think you need to override getDropDownView to deal with spinners
#Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
BusRoute route = getItem(position);
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.bus_route, parent, false);
}
TextView tvBusRoute = (TextView) convertView.findViewById(R.id.tvBusRoute);
TextView tvBusRouteNumber = (TextView) convertView.findViewById(R.id.tvBusRouteNumber);
tvBusRoute.setText(route.routeName);
tvBusRoute.setTag(route.route);
tvBusRouteNumber.setText(route.route);
if (!route.routeColor.equals("")) {
tvBusRouteNumber.setBackgroundColor(Color.parseColor(route.routeColor));
}
return convertView;
}

Aligning Selected Spinner Item

I'm trying to make a slight adjustment to the positioning of the selected item in the spinner. Not the dropdown list items, as I already have a custom view in my adapter for that, but the selected item specifically.
As you can see in the screenshot, "Any" is the currently selected item. But it is aligned oddly within the container because it has to accommodate the longest string in the dropdown, which is "Dark Purple Burnt Sienna" (or whatever). I want to align the selected text to the right so that "Any" is next to the dropdown indicator instead of way out in the middle.
I've attempted to make adjustments to my custom spinner-item view, but it doesn't have any affect on the selected item.
I've also attempted to set gravity and text alignment on the Spinner itself, but it has no effect.
Here's the image and the xml:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/default_black"
android:layout_centerVertical="true"
android:text="Color" />
<Spinner
android:id="#+id/spn_color"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"/>
</RelativeLayout>
Edit: Here's my adapter:
public class ColorsAdapter<T> implements SpinnerAdapter {
ArrayList<String> mColors;
ArrayList<Integer> mValues;
Context mContext;
public ColorsAdapter(ArrayList<String> colors, ArrayList<Integer> values,
Context context) {
mContext = context;
mColors = colors;
mValues = values;
}
#Override
public int getCount() {
return mColors.size();
}
#Override
public Object getItem(int position) {
return mColors.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return R.layout.list_item_color;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView v = new TextView(mContext);
v.setText(mColors.get(position));
return v;
}
#Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
Context.LAYOUT_INFLATER_SERVICE );
View row = inflater.inflate(getItemViewType(position), parent, false);
TextView tvName = (TextView)row.findViewById(R.id.tv_name);
tvName.setText(mColors.get(position));
row.setTag(mValues.get(position));
return row;
}
#Override
public int getViewTypeCount() {
return 1;
}
#Override
public boolean hasStableIds() {
return false;
}
#Override
public boolean isEmpty() {
return false;
}
}
And here's the XML for the list item:
<TextView
android:id="#+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:gravity="right"/>
You can adjust the settings for selected item in getView(int position, View convertView, ViewGroup parent) method. If you just want to set the gravity and text alignment, you should set it to the TextView in the method like
#Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView v = new TextView(mContext);
v.setText(mColors.get(position));
v.setGravity(Gravity.END); // OR v.setGravity(Gravity.RIGHT);
return v;
}
Or you can simply inflate custom layout if you want to make further customization:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
View row = null;
if (inflater != null) {
row = inflater.inflate(R.layout.list_item_view_color, parent, false);
TextView textView = row.findViewById(R.id.textview);
textView .setText(mColors.get(position));
}
return row;
}
Where list_item_view_color.xml is your layout file for the selected value.
(NOTE: If you want to use options like autoSizeTextType, you can use them with app prefix for AppCompatTextView in xml files)
Simple solution would be to build another Custom view for the spinner TextView layout, and specify the gravity for your TextView in that. Something like :
<!--spinner_layout.xml (in layout/)-->
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right"
/>
Use that in initalising the ArrayAdapter of your spinner :
ArrayAdapter<String> adapter = new ArrayAdapter<String>
(this, android.R.layout.spinner_layout,
spinnerArray);
UI output :
You can creating the TextView for the getView method programmatically so no property for alignment is being set, easy thing to do is inflate it in the same way you are doing it on the getDropdownView, or simply set the proper gravity in the view you are instantiated.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView v = new TextView(mContext);
v.setText(mColors.get(position));
v.setGravity(Gravity.RIGHT);
return v;
}
You can try this
android:autoSizeTextType="uniform"
Padding, Margins and Alignments get confusing when it comes to Spinners. I ran into a similar issue as well. But for me I wasn't using a custom view and adapter, so I managed to use styles to get around this issue.
For you since you are already using custom view and adapter, you can make the background null for the spinner so that the dropdown arrow will not be visible.
android:background="#null"
Then in the TextView, you can assign drawableRight to be the dropdown arrow.
android:drawableRight="#drawable/ic_spinner_drop_arrow_white_24dp"
The icon can be a dropdown vector drawable. Also to specify particular margin between the arrow and text, you should be able to use drawablePadding
Hope this helps.
I can give you a method that may work.
In XML
1) add a new text view called the_current_color text view
2) allign this text view above the spinner and to the start of the arrow icon
or wherever you want it to be.
In your code
1) when you listen to (on item selected ) from the spinner simply get the current text and set it in the (the_current_color) text view.
2) and try to hide the texts in the spinner by changing color to transparent.
Hope it works
Edit
I might also suggest something maybe it changes things
lets say that we have a spinner that uses this string resource file to populate its adapter
<string-array name="my_adapter">
<item>Blue </item>
<item>Red</item>
</string-array>
until now we have a spinner that shows (red item) and (blue item) at the far left from the arrow icon of the spinner.
create another string resources now like that
<string-array name="my_adapter_two">
<item> Blue </item>
<item> Red</item>
</string-array>
as you can see now the added space will give an illusion that the strings are beside the arrow icon
you can add space that fit your need and you can maybe switch the files after you click a certain item and then switch them back.
You can try the following Custom view.
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/textView1"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:gravity="right"
android:padding="15dp"
android:textAlignment="gravity"
android:textColor="#color/black"
android:textSize="10dp" />

Changing background color of ListView item upon clicking a Button

Apologies if this question has been asked previously.
I have a ListFragment that contains a Button in each list item. Clicking on the Button should change the background color of that particular list item. I understand that the ListView inside the ListFragment refreshes itself implying that if a user scrolls it is likely that a list item that was not previously clicked will have its background color changed.
Many of the solutions I have come across involve storing the position of the list item that was clicked in an overridden getView(int position, View convertView, ViewGroup parent) method implemented in a custom adapter (that extends SimpleAdapter in my case).
However my problem is compounded by the presence of a Button inside every list item implying that the Button will not be clickable unless I attach an OnClickListener inside a custom adapter (this part is done). Now, trying to store the position of the Button's list item fails because the saved position is always a refreshed position and not the absolute position of the list item. I also tried setting tags for each button but even those get recycled as the page scrolls, which is (unfortunately) expected behaviour considering the design of ListView.
I understand that this problem is easily solvable if the a list item does not have children that need to be clicked. But what if there are children within every list item that need to respond to clicks separate from the parent view that contains each list item ? I have also tried using selectors to manipulate a list item's background but that hasn't helped either.
Here is my Custom Adapter's getView method:
#Override
public View getView(int position, View convertView, ViewGroup parent){
View view = convertView;
final int pos = position;
if(view == null){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.fragment_layout, null);
Button button = (Button) view.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
Log.d("CLICKED POSITION",Integer.valueOf(pos).toString());
View parent = (View) v.getParent();
parent.setBackgroundColor(Color.GREEN);
}
});
}
return view;
}
And the XML layout file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="300dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_weight="1"
android:descendantFocusability="blocksDescendants"
android:layout_margin="5dp"
>
<ListView android:id="#id/android:list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/label"
android:layout_gravity="start"
/>
<TextView
android:id="#+id/text_id"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="end"
/>
<Button
android:id="#+id/button"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="#string/button"
android:layout_margin="2dp"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:clickable="true"
/>
</LinearLayout>
I have spent many hours on this problem and I am unable to arrive at a solution. Any direction, guidance or a solution would be most appreciated.
In your Custom Adapter's getView() method, you are inflating the view and attaching the onclicklistener only if the convertView is null. convertView IS the recycled view. If convertView is not null, then you need to change the values in that view. Your getView() method should be like this instead
#Override
public View getView(int position, View convertView, ViewGroup parent){
View view = convertView;
final int pos = position;
//No recycled view, so create a new one
if(view == null){
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.fragment_layout, null);
}
//By this line, we either have a view that is created in the if block above or passed via convertView
Button button = (Button) view.findViewById(R.id.button);
//attach listener to the view, recycled or not
button.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
Log.d("CLICKED POSITION",Integer.valueOf(pos).toString());
//parent not required as we are have the view directly
//View parent = (View) v.getParent();
v.setBackgroundColor(Color.GREEN);
}
});
return view;
}
Indeed you should also be changing any values associated with that item according to that position, if they change. For example, if each button displays the position in the list, you should also add the following line
button.setText("Button " + position);

Android GridView Item Selectors change position and duplicate

I have a GridView layout with 3 columns and several rows that contains pictures taken from Facebook. When I select an image, a selector appears for that item in the gridView as normal. (I don't use the android selector but a custom layout linked below.)
The problem is this: if I select one or more items, when I scroll down the screen to see more pictures I discover that other items have already been selected! Moreover, if I scroll up to see the items I've previously selected, the selectors change their position. It seems to me that the index of each selector update every time I scroll the screen.
I created a personal ImageAdapter to inflate the pictures from facebook. Here the getView:
public View getView(final int position, View convertView, ViewGroup parent) {
View v;
ImageView imageView = null;
if(convertView==null){
LayoutInflater li = getLayoutInflater();
v = li.inflate(R.layout.photos_item, null);
}else{
v = convertView;
}
imageView = (ImageView)v.findViewById(R.id.image);
v.setTag(imageView);
if (arrayPhotos.get(position).getPictureSource() != null){
Log.i(TAG, "Position in GridView = " + Integer.valueOf(position).toString());
imageLoader.DisplayImage(arrayPhotos.get(position).getPictureSource(), imageView);
}
return v;
}
Here the onItemClick:
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
View checkedItem;
checkedItem = (View) view.findViewById(R.id.check);
fbPicture = arrayFbPictures.get(position);
Log.i(TAG, "Position = " + arrayFbPictures.indexOf(fbPicture));
Log.i(TAG, "PicId = " + fbPicture.getPictureID());
if( fbPicture.isChoosed() ){
Log.i(TAG, "checkIcon DISACTIVATED");
fbPicture.setChoosed(false);
checkedItem.setVisibility(View.INVISIBLE);
parent.invalidate();
}
else{
Log.i(TAG, "checkIcon ACTIVATED");
fbPicture.setChoosed(true);
checkedItem.setVisibility(View.VISIBLE);
parent.invalidate();
}
}
That is the xml of the GridView:
<GridView
android:id="#+id/gridview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:gravity="center"
android:numColumns="auto_fit"
android:columnWidth="96dp"
android:stretchMode="spacingWidth"
android:horizontalSpacing="8dp"
android:verticalSpacing="8dp"
android:paddingTop="8dp"
android:paddingLeft="8dp"
android:paddingRight="8dp" >
</GridView>
And this the xml of the custom selector:
<RelativeLayout
android:id="#+id/check"
android:layout_width="96dp"
android:layout_height="96dp"
android:background="#color/black_trasparency_light"
android:visibility="invisible" >
<ImageView
android:id="#+id/image_check"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:scaleType="center"
android:src="#drawable/ic_check_colored_48" />
</RelativeLayout>
Thank you.
The reason this is happening is because GridView uses view recycling. When you scroll down the GridView, instead of creating new views to put in the bottom, GridView reuses the ones that disappear from the top. What this means is that you have to remember to fully set the state of the view inside the call to getView().
That's a little hard to digest, so here's some code:
public View getView(final int position, View convertView, ViewGroup parent) {
...
View checkedItem = (View) v.findViewById(R.id.check);
fbPicture = arrayFbPictures.get(position);
if (fbPicture.isChoosed()) {
checkedItem.setVisibility(View.VISIBLE);
} else {
checkedItem.setVisibility(View.INVISIBLE);
}
return v;
}
The code above is setting the checked state of the convertView every time getView() is called, so the recycled view will not retain any of its previous state.
PS. The is done to conserve the memory and to improve performance of GridView and ListView. You can watch this awesome presentation for more information.

Add a new item to a listview

I am not quite sure my question really has something to do with listview.There is an app named Gleeo Time Tracker ,and here has a screenview of it.When you press the symbol "+",a new item will be created,and you can delete one item by pressing the "-".More is that when I click the record button on the left of the item,the background will change.
My question is,what is it in the end,a listview? How can I achieve such thing?Thank you all!
What you want to do, is build a custom ListView.
This is done by providing a layout file, for example
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#android:color/transparent">
<ImageView android:id="#+id/Plus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/transparent" />
<TextView android:id="#+id/Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/transparent" />
</LinearLayout>
This layout is then applied to each row of your ListView by overriding its Adapter's getView() method, for example something like this:
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) MyActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.row, null);
}
ImageView plus = (ImageView) v.findViewById(R.id.plus);
icon.setImageDrawable(BrowseActivity.this.getResources().getDrawable(R.drawable.plus));
TextView title = (TextView) v.findViewById (R.id.Browse_FileName);
// add a listener to your button and you're done
return v;
}
read about list view in developer docs and here is an example

Categories

Resources