Thanks for reading!
Some background:
I am building a Gallery app from the tutorial here
Only change I made to this code is to replace
i.setLayoutParams(new Gallery.LayoutParams(150, 100));
with
i.setLayoutParams(new Gallery.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
to display only one gallery image at a time (Kinda like a slideshow viewer).
Problem:
I want the first thumbnail of the gallery to act as an Album Cover with two TextView's to display Album info.
Experiment:
So, I created an cover.xml like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:padding="6dip">
<ImageView android:id="#+cover/imgImage" android:adjustViewBounds="true"
android:layout_width="fill_parent" android:layout_height="fill_parent">
</ImageView>
<TextView android:id="#+cover/tvCoverText1"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:maxLines="2" android:text="Text1" />
<TextView android:id="#+cover/tvCoverText2"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:singleLine="true" android:maxLines="1" android:layout_below="#cover/tvCoverText1"
android:text="Text2" />
</RelativeLayout>
And here's the Java code. I check in getView() if the position is 0 (the first thumbnail) and then play around with the views.
package com.sagar.sample;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
public class Main extends Activity {
private LayoutInflater mInflater = null;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Gallery g = (Gallery) findViewById(R.main.gallery);
g.setAdapter(new ImageAdapter(this));
g.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView parent, View v, int position, long id) {
Toast.makeText(Main.this, "" + position, Toast.LENGTH_SHORT).show();
}
});
}
public class ImageAdapter extends BaseAdapter {
int mGalleryItemBackground;
private Context mContext;
private Integer[] mImageIds = {
0, R.drawable.bp1, R.drawable.bp2, R.drawable.bp3
};
public ImageAdapter(Context c) {
mContext = c;
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// TypedArray a = obtainStyledAttributes(R.styleable.HelloGallery);
// mGalleryItemBackground = a.getResourceId(
// R.styleable.HelloGallery_android_galleryItemBackground, 0);
// a.recycle();
}
public int getCount() {
return mImageIds.length;
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
View view = convertView;
if(view == null) {
view = mInflater.inflate(R.layout.cover, null);
viewHolder = new ViewHolder();
viewHolder.tvCoverText1 = (TextView)view.findViewById(R.cover.tvCoverText1);
viewHolder.tvCoverText2 = (TextView)view.findViewById(R.cover.tvCoverText2);
viewHolder.imgView = (ImageView)view.findViewById(R.cover.imgImage);
view.setTag(viewHolder);
}
else {
viewHolder = (ViewHolder)view.getTag();
}
if(position == 0) {
viewHolder.tvCoverText1.setVisibility(View.VISIBLE);
viewHolder.tvCoverText2.setVisibility(View.VISIBLE);
viewHolder.imgView.setVisibility(View.GONE);
}
else {
viewHolder.tvCoverText1.setVisibility(View.GONE);
viewHolder.tvCoverText2.setVisibility(View.GONE);
viewHolder.imgView.setVisibility(View.VISIBLE);
//viewHolder.imgView = new ImageView(mContext);
viewHolder.imgView.setImageResource(mImageIds[position]); //Album cover is at 0th position
viewHolder.imgView.setLayoutParams(new Gallery.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
viewHolder.imgView.setScaleType(ImageView.ScaleType.FIT_XY);
viewHolder.imgView.setBackgroundResource(mGalleryItemBackground);
}
return view;
}
}
static class ViewHolder {
TextView tvCoverText1, tvCoverText2;
ImageView imgView;
}
}
End Result:
When the app loads up, I first see a blank screen for a while and then the view changes to display the AlbumCover. And it's painfully slow to scroll across the images.
Hmm..obviously, I am doing something wrong. I sincerely hope someone could help me here :(
Thanks!
UPDATE: Adding main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<Gallery
android:id="#+main/gallery"
android:layout_width="fill_parent" android:layout_height="fill_parent"
/>
</RelativeLayout>
UPDATE2: So, here's some psuedo code to explain what I am trying to achieve:
if(position == 0)
//show toptext and bottomtext from cover.xml
else
//show tvTitle1 and tvTitle2 (may later include tvTitle3 and tvTitle4) from main.xml
Right now, only the position 0 case works and that too when I swipe to position 1 and swipe back to position 0 - the TextViews are grayed out and barely visible. :(
You need to set the text in your textviews everytime and not only when you inflate the view.
You should probably have 1 single layout file which holds the ui components in the gallery. Right now you have 2 TextView components which are independent of the Gallery. Instead, create some layout resource like this:
gallery_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:id="#+id/imageView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:adjustViewBounds="true"
android:background="#android:drawable/picture_frame"
android:layout_centerInParent="true" />
<TextView
android:id="#+main/tvTitle1" android:text="Title 1"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_above="#+main/tvTitle2">
</TextView>
<TextView
android:id="#+main/tvTitle2" android:text="Title 2"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" android:layout_alignParentLeft="true">
</TextView>
</RelativeLayout>
So in your getView:
View v = convertView;
if(v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.gallery_item, null);
holder = new ViewHolder();
holder.text1 = (TextView)v.findViewById(R.id.tvTitle1);
holder.text2 = (TextView)v.findViewById(R.id.tvTitle2);
holder.imageView = (ImageView)v.findViewById(R.id.imageView);
v.setTag(holder);
}
else
holder = (ViewHolder)v.getTag();
if(position == 0) {
holder.text1.setVisibility(View.VISIBLE);
holder.text2.setVisibility(View.VISIBLE);
holder.imageView.setVisibility(View.GONE);
}
else {
holder.text1.setVisibility(View.GONE);
holder.text2.setVisibility(View.GONE);
holder.imageView.setVisibility(View.VISIBLE);
}
return v;
You may find that there is some strange behavior going on when you scroll through items, so you might have to directly access each UI component rather than using the holder:
((TextView)v.findViewById(R.id.tvTitle1)).setVisibility(View.GONE);, etc
You also may be interested in setting different types of views for getView: http://developer.android.com/reference/android/widget/Adapter.html#getItemViewType(int)
After trying all approaches suggested here, I wasn't able to still get a custom gallery the way I wanted it to. I kept running into ClassCastException. So, here's what worked for me till now. This is just a workaround and incase someone comes up with a better way to design a custom gallery - do post your answer here so I can accept it.
Thanks for helping out!
Related
My android layout with grid view is not showing properly on 480 x 854 pixels (~196 ppi pixel density) device.and please suggest me is there any method to work with layout creation at run time.
Android categorizes device screens using two general properties: size
and density. You should expect that your app will be installed on
devices with screens that range in both size and density.
Create Different Layouts
To optimize your user experience on different screen sizes, you should create a unique layout XML file for each screen size you want to support. Each layout should be saved into the appropriate resources directory .
More info you can visit official guideline
Supporting Different Screen Sizes
#N.shah , For Gridview, You should make all view dynamic, So that all the cells come exactly on each and every device
How We can do this
Like we have 4 rows and 2 columns
All 4 rows should show on window
1. Find out the device width and height
2. Lets say height is X. Find out height of status bar i.e Y and action bar Z
3. Find out the exact height where we have to show our gridview i.e
X=X-(Y+Z)
4. Divide the X by no. of rows you want to show to find out row height i.e R
R=X/4( where 4 is no. of rows)
5. Create an xml which you want to show in gridview
custom_gridview
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/ll_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_marginBottom="#dimen/menu_grid_vertical_spacing"
android:orientation="vertical"
android:weightSum="4.5" >
<View
android:id="#+id/view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.9"
android:gravity="center" >
<ImageView
android:id="#+id/img_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="#string/app_name"
android:padding="#dimen/menu_page_padding"
android:src="#drawable/ic_launcher" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.4" />
<com.slinfy.ikharelimiteduk.custom.CustomTextViewSegoeUISemiBold
android:id="#+id/txt_name"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.2"
android:gravity="top|center_horizontal"
android:text="#string/app_name"
android:textColor="#android:color/white"
android:textSize="#dimen/app_menu_text_size" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="#dimen/menu_grid_vertical_spacing"
android:layout_alignParentBottom="true"
android:background="#2E3A60" />
</RelativeLayout>
Make an adapter
GridViewAdapter
import com.slinfy.ikharelimiteduk.R;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
#SuppressLint({ "ViewHolder", "InflateParams" })
public class GridViewAdapter extends BaseAdapter {
String[] mMenuNames;
int[] mColors;
Integer[] mMenuIcons;
Context mContext;
private LayoutInflater inflater;
float mGridSize;
float mPadding;
int noOfColums = 4;
public GridViewAdapter(String[] menuNames, Context context, int[] colors, Integer[] menuIcons, float gridSize) {
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mMenuNames = menuNames;
mMenuIcons = menuIcons;
mColors = colors;
mContext = context;
mGridSize = gridSize;
mPadding = context.getResources().getDimension(R.dimen.menu_grid_vertical_spacing);
}
#Override
public int getCount() {
return mMenuIcons.length;
}
#Override
public Object getItem(int arg0) {
return null;
}
#Override
public long getItemId(int arg0) {
return 0;
}
#Override
public View getView(int index, View arg1, ViewGroup arg2) {
ViewHolder holder = null;
if (arg1 == null) {
holder = new ViewHolder();
arg1 = inflater.inflate(R.layout.custom_gridview, null);
holder.imgMenu = (ImageView) arg1.findViewById(R.id.img_menu);
holder.txtName = (TextView) arg1.findViewById(R.id.txt_name);
holder.layout = (RelativeLayout) arg1.findViewById(R.id.layout);
arg1.setTag(holder);
} else {
holder = (ViewHolder) arg1.getTag();
}
holder.txtName.setText(mMenuNames[index]);
holder.imgMenu.setImageResource(mMenuIcons[index]);
holder.layout.setBackgroundResource(mColors[index]);
holder.layout.setLayoutParams(new GridView.LayoutParams(GridView.AUTO_FIT,
(int) ((mGridSize / noOfColums) - 5/* - (mPadding) - 6 */)));
return arg1;
}
class ViewHolder {
TextView txtName;
ImageView imgMenu;
RelativeLayout layout;
}
}
Set the adapter in your main class
GridView grid = (GridView) findViewById(R.id.grid_view);
grid.setAdapter(new GridViewAdapter(menuNames, getApplicationContext(), colors, menuIcons, gridSize));
Alright, so I'm using a ListView with a custom adapter. Everything works fine and dandy...until the user selects a ListView row and tries to scroll.
When the user selects a row, the background color of that row changes to blue (which is good).
But, problems occur when we begin scrolling: When we scroll past the selected row, the blue fixes itself to either the bottom or the top of the ListView, depending on which way we were scrolling.
Selected row changes color on touch (good)
Part of the background of selected row is fixed to top when scrolling down (not good)
Part of the background of selected row is fixed to bottom when scrolling up (not good)
Here is my source code:
List View that I'm populating dynamically
<ListView
android:id="#+id/tallyDataListView"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:divider="#000000"
android:dividerHeight="1dp"
android:fadeScrollbars="false"
android:listSelector="#0099FF" >
layout_list_view_row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<View
android:focusable="false"
android:focusableInTouchMode="false"
style="#style/tableSideBorderLine" />
<TextView
android:id="#+id/COLUMN_PIPE_NUMBER"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
style="#style/tableColumn"
xmlns:android="http://schemas.android.com/apk/res/android" />
<View
android:focusable="false"
android:focusableInTouchMode="false"
style="#style/tableColumnDivider" />
<TextView
android:id="#+id/COLUMN_TOTAL_LENGTH"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
style="#style/tableColumn"
xmlns:android="http://schemas.android.com/apk/res/android" />
<View
android:focusable="false"
android:focusableInTouchMode="false"
style="#style/tableColumnDivider" />
<TextView
android:id="#+id/COLUMN_ADJUSTED"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
style="#style/tableColumn"
xmlns:android="http://schemas.android.com/apk/res/android" />
<View
android:focusable="false"
android:focusableInTouchMode="false"
style="#style/tableSideBorderLine" />
</LinearLayout>
My Custom Adapter
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import java.util.ArrayList;
public class ListViewAdapter extends ArrayAdapter<String>{
LayoutInflater inflater;
private final ArrayList<String> adjustedValues;
private final ArrayList<String> pipeNumbers;
private final ArrayList<String> totalLengthValues;
public ListViewAdapter(Activity pContext, ArrayList<String> pPipeNumbers,
ArrayList<String> pTotalLengthValues, ArrayList<String> pAdjustedValues)
{
super(pContext, R.layout.layout_list_view_row, pAdjustedValues);
adjustedValues = pAdjustedValues;
pipeNumbers = pPipeNumbers;
totalLengthValues = pTotalLengthValues;
inflater = pContext.getLayoutInflater();
}
#Override
public View getView(int pPosition, View pView, ViewGroup pParent)
{
View view = inflater.inflate(R.layout.layout_list_view_row, pParent, false);
TextView col1 = (TextView)view.findViewById(R.id.COLUMN_PIPE_NUMBER);
col1.setText(pipeNumbers.get(pPosition));
TextView col2 = (TextView)view.findViewById(R.id.COLUMN_TOTAL_LENGTH);
col2.setText(totalLengthValues.get(pPosition));
TextView col3 = (TextView)view.findViewById(R.id.COLUMN_ADJUSTED);
col3.setText(adjustedValues.get(pPosition));
return view;
}
}
This is the common problem about the listview. When you scroll down it creates the new view every time. That is why the selected element from the top gets out of the focus and another element is selected.
For this problem you have to extend the BaseAdapter class and
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Vehical vehical = vehicals.get(position);
ViewHolder viewHolder = null;
if(convertView==null)
{
viewHolder = new ViewHolder();
convertViewactivity.getLayoutInflater().inflate(R.layout.list_item,null);
convertView.setTag(viewHolder);
}
else
{
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.tvVehicalName = (TextView) convertView.findViewById(R.id.vehicle_name);
viewHolder.tvVehicalName.setText(vehical.getVehicalName());
if(vehical.isSelected()){
viewHolder.tvVehicalName.setTextColor(Color.RED);
}
else
{
viewHolder.tvVehicalName.setTextColor(Color.BLACK);
}
return convertView;
}
//On listener of the listview
searchList.setOnItemClickListener(
new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position, long arg3) {
if(searchAdapter.isItemSelected(position))
{
searchAdapter.setSelectedItem(position,false);
selectedList.remove(((Vehical)searchAdapter.getItem(position)).getVehicalName());
}
else
{
if(selectedList.size()<new_vehiclelimit){
searchAdapter.setSelectedItem(position,true);
selectedList.add(((Vehical)searchAdapter.getItem(position)).getVehicalName());
}
else
{
Toast.makeText(getApplicationContext(), "Vechicle Limit is Over", Toast.LENGTH_SHORT).show();
}
}
Keep a reference for selected row position in your Adapter, say
int selectedPos = -1;
The value will be -1 when no row is selected. And in the OnItemClickListener of the listview,update selectedPos with the clicked position and call notifyDatasetChanged() on the adapter. In the getView method of the adapter, check for the selectedPos value and highlight the row accordingly.
EDIT: This problem has been resolved, so the code in my original post does not apply anymore. Thanks to everyone who contributed.
First off, I have checked other similar questions but they either don't match mine or use the BaseAdapter. What I'm trying to do is display a text-image combination as part of a GridView. The actual app is larger, but my problem is that the GridView is blank in the very first activity!
I'd appreciate if someone can take a quick look at it and tell me what's wrong.
Code for layout file used for GridView elements:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical" >
<ImageView
android:id="#+id/iv_grid"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="#drawable/ic_launcher" />
<TextView
android:id="#+id/tv_grid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView" />
</LinearLayout>
Layout file of first Activity:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".CategoriesActivity" >
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="54dp"
android:text="Select a category to learn more" />
<TextView
android:id="#+id/tv_grid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="26dp"
android:text="Lessons from History"
android:textSize="18sp" />
<GridView
android:id="#+id/gridView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/textView2"
android:layout_marginTop="25dp"
android:numColumns="3" >
</GridView>
</RelativeLayout>
And finally, the Java file:
package com.example.historicpersonalities;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;
public class CategoriesActivity extends Activity {
GridView gv_categories;
public int selected; //which category is selected [0-4]?
String[] data; //holds the data for GridView
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_categories);
populate();
selected = -1;
gv_categories = (GridView) findViewById(R.id.gridView1);
gv_categories.setAdapter(new MyAdapter(this,
android.R.layout.simple_expandable_list_item_1));
gv_categories.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int index,
long arg3) {
}
});
}
class MyAdapter extends ArrayAdapter<String> {
public MyAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.layout_grid_scheme, null);
TextView item = (TextView) findViewById(R.id.tv_grid);
item.setText(data[position]);
ImageView img = (ImageView) findViewById(R.id.iv_grid);
img.setImageResource(R.drawable.dummy);
Log.d("ankush", ""+position);
return v;
}
}
private void populate() {
data = new String[5];
data[0] = "Writers";
data[1] = "Painters";
data[2] = "Conquerors";
data[3] = "Chemists";
data[4] = "Actors";
}
}
I think the problem is the height of your gridView. GridView is a scrollable container and can contain any amount of content in any height. Try to set background color to your gridView and you will see, that there is no gridview on the screnn.
It's because you use wrap_content for gridview, but it's incorrect.
If I'm not wrong, it seems that in your constructor you don't pass any data to the adapter.
public MyAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
In this code snippet, your are using ArrayAdapter (Context context, int textViewResourceId) which initialize an empty arraylist as the objects to represent in the GridView.
public ArrayAdapter(Context context, int textViewResourceId) {
init(context, textViewResourceId, 0, new ArrayList<T>());
}
Therefore your GridView should be empty. In addition to that, it seem you didn't properly implement the getView.(findViewById() parts should be handled as shown in the Opiatefuchs's answer)
If you want to extend ArrayAdapter, you should be calling a constructor which takes an array of objects as a parameter. (Of course there are many ways to make a custom adapter you can extend BaseAdapter or while extending ArrayAdapter you can override some other methods)
This way it should work:
class MyAdapter extends ArrayAdapter<String> {
public MyAdapter(Context context, int textViewResourceId, String[] objects) {
super(context, textViewResourceId, objects);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.layout_grid_scheme, null);
} else {
view = convertView;
}
TextView item = (TextView) view.findViewById(R.id.tv_grid);
item.setText(getItem(position));
ImageView img = (ImageView) view.findViewById(R.id.iv_grid);
img.setImageResource(R.drawable.dummy);
}
}
Don´t know if this causes the problem, but You have to set Your TextView and ImageView in Your adapter like this:
TextView item = (TextView) v.findViewById(R.id.tv_grid);
item.setText(data[position]);
ImageView img = (ImageView) v.findViewById(R.id.iv_grid);
img.setImageResource(R.drawable.dummy);
EDIT
First of all, the best way is to combine all three solutions. BUT, I have seen another issue in Your xml layout: You put the same id to the TextView inside Your GridView Element xml and Your Activity layout xml. Both TextViews got id="#+id/tv_grid" . This could never work, change the id´s to different ones
I'm having an issue getting my list populated correctly by my custom ArrayAdapter (code below). As I understand it, my adapter is only populating the textviewResourceId when it is instanciated since I'm using constructor Adapter(context, rowLayout, textViewResourceId, ArrayList<Items>), but the getView method is only called when rows that were not visible become visible.
This is causing an issue as, when my list is first showing, only the title of my article is showing, and I have to scroll all the way down the list and up for all the views in each row to be populated correctly (since that task is done in getView).
Can anyone point me in the right direction? How could I refactor this so all views in each visible row gets populated right away?
Code to my custom adapter:
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class ArticleArrayAdapter extends ArrayAdapter<Article> {
private final Context context;
private final ArrayList<Article> articles;
#SuppressWarnings("unused")
private final int rowLayout;
public ArticleArrayAdapter(Context context, int rowLayout, int textViewResourceId, ArrayList<Article> articles) {
super(context, rowLayout, textViewResourceId, articles);
this.rowLayout=rowLayout;
this.context = context;
this.articles = articles;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(R.layout.affichageitem, null);
}
else {
TextView viewTitre = (TextView)row.findViewById(R.id.titre);
TextView viewAuteur = (TextView)row.findViewById(R.id.auteur);
TextView viewDate = (TextView)row.findViewById(R.id.date);
ImageView viewLogo = (ImageView)row.findViewById(R.id.category_logo);
viewTitre.setText(articles.get(position).getTitle());
viewAuteur.setText(articles.get(position).getCreator());
viewDate.setText(articles.get(position).getDate());
Drawable drawLogo = context.getResources().getDrawable(R.drawable.logocat);
viewLogo.setImageDrawable(drawLogo);
}
return super.getView(position, convertView, parent);
}
}
Edited version:
public class ArticleArrayAdapter extends ArrayAdapter<Article> {
private final Context context;
#SuppressWarnings("unused")
private final int rowLayout;
public ArticleArrayAdapter(Context context, int rowLayout,int textViewResourceId) {
super(context, rowLayout, textViewResourceId);
this.rowLayout=rowLayout;
this.context = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(R.layout.affichageitem, null);
}
else {
TextView viewTitre = (TextView)row.findViewById(R.id.titre);
TextView viewAuteur = (TextView)row.findViewById(R.id.auteur);
TextView viewDate = (TextView)row.findViewById(R.id.date);
ImageView viewLogo = (ImageView)row.findViewById(R.id.category_logo);
viewTitre.setText(getItem(position).getTitle());
viewAuteur.setText(getItem(position).getCreator());
viewDate.setText(getItem(position).getDate());
Drawable drawLogo = context.getResources().getDrawable(R.drawable.logocat);
viewLogo.setImageDrawable(drawLogo);
}
return super.getView(position, convertView, parent); // <<- ONLY TITLES
//return row; <<- EMPTY
}
}
rowLayout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<ImageView
android:id="#+id/category_logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:contentDescription="#string/logo_desc"
android:padding="20dp" />
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:paddingLeft="5dp" >
<TextView
android:id="#+id/titre"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="18dp"
android:textStyle="bold" />
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity=""
android:orientation="horizontal" >
<TextView
android:id="#+id/auteur"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:textSize="12dp" />
<TextView
android:id="#+id/espace"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/espace"
android:adjustViewBounds="true"
android:textSize="12dp" />
<TextView
android:id="#+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:textSize="12dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
The problem is that you are not populating new views. What happens is that Android may keep a fixed number of views which will be used for your list view. The views are recycled which is why it's impossible to "populate" all your views before they become visible. This line
if (view == null) {
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(R.layout.affichageitem, null);
}
checks whether a view is being recycled or not. null means it's not, so if you get null you need to inflate a new view. Upto there your code's fine. However, you need to populate the view whether it's a newly inflated view or not. So you shouldn't have the else statement, just have
View row = convertView;
if (row == null) {
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(R.layout.affichageitem, null);
}
TextView viewTitre = (TextView)row.findViewById(R.id.titre);
TextView viewAuteur = (TextView)row.findViewById(R.id.auteur);
TextView viewDate = (TextView)row.findViewById(R.id.date);
ImageView viewLogo = (ImageView)row.findViewById(R.id.category_logo);
viewTitre.setText(getItem(position).getTitle());
viewAuteur.setText(getItem(position).getCreator());
viewDate.setText(getItem(position).getDate());
Drawable drawLogo = context.getResources().getDrawable(R.drawable.logocat);
viewLogo.setImageDrawable(drawLogo);
return row;
The reason why it worked when you scrolled all the way down is that on your way back up getView was receiving recycled views and it jumped right into the else clause you had.
You are using your own collection ArrayList<Article>.
Note that every ArrayAdaper<foo> already has data collection built in, where you can add by add(foo) or addAll(List<foo>) and clear it by clear() method.
Also, ListView can observe this data and refresh when changes happen to this data. Or explicitly when notifyDataSetChanged() is called on adapter.
Problem here is that you are accepting data in constructor, storing in yet another local variable, and notifyDataSetChanged() is not being called. You cannot call it from constructor as Object is still under construction.
So, Don't accept data in constructor. Inside getView() use getItem(position) to get Article item.
Add data externally like:
ArticleArrayAdapter adapter = new ArticleArrayAdapter(context,rowLayout,android.R.layout.simple_list_item_1);
adapter.addAll(articles);
myListView.setAdapter(adapter);
Looks correct to me - only thing: there is no need to call the superclass. Try to return your assembled view like that:
return row;
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row==null){
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
row = inflater.inflate(R.layout.affichageitem, null);
convertView.setTag(row);
}else{
TextView viewTitre = (TextView)row.findViewById(R.id.titre);
TextView viewAuteur = (TextView)row.findViewById(R.id.auteur);
TextView viewDate = (TextView)row.findViewById(R.id.date);
ImageView viewLogo = (ImageView)row.findViewById(R.id.category_logo);
viewTitre.setText(getItem(position).getTitle());
viewAuteur.setText(getItem(position).getCreator());
viewDate.setText(getItem(position).getDate());
Drawable drawLogo = context.getResources().getDrawable(R.drawable.logocat);
viewLogo.setImageDrawable(drawLogo);
}
}
I had this similar kind of issue. I also found that without scrolling manually to the last of the list all views were not created. All items were null except visible items.So, I had to scroll down to the last of the list programmatically.
I have extended MyAdapter from BaseAdapter class.
mAdapter = new MyAdapter(this, mFinalContactList);
mListView.setAdapter(mAdapter);
To auto scrolling to last with all listview created I have used following lines of code.
mAdapter.notifyDataSetChanged();
mListView.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
mListView.smoothScrollToPosition(mFinalContactList.size()-1);
This might help some one who are trying auto scroll to the last.
N.B. mListView.setSelection(position); method only can point the last list item if we set the position to last. But can not populate the list item from forst to last.
Thank you
i have to make a listview that haves a list of names, and also, aligned to the left, but in the same field, the sex of the person, male or female
is possible to do it? how?
code examples welcome
EDIT
I try with the first user answer, and i got this exception: 12-14 22:39:56.191: ERROR/AndroidRuntime(917): java.lang.IllegalStateException: ArrayAdapter requires the resource ID to be a TextView
this is the code of the XML item i make:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Left side"
android:layout_alignParentLeft="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Right side"
android:layout_alignParentRight="true"/>
</RelativeLayout>
and this is the code where i have my list:
public class PendingInvitations extends ListActivity {
......
.....
....
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
private List<String> usernames=new ArrayList<String>();
for (int i=0;i<friends.size();i++)
{
usernames.add(i,friends.get(i).getFullName());
}
setListAdapter(new ArrayAdapter<String>(this, R.layout.list_item2, usernames));
this would be the view that is used for each cell
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" >
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Left side" android:layout_alignParentLeft="true"/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Right side" android:layout_alignParentRight="true"/>
</RelativeLayout>
this is an example since i have no idea where your knowledge is at with lists, if the above xml was called "temp.xml" you would use this in the setlistadapter function
import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
public class FooList extends ListActivity extends BaseAdapter {
String[] listItems = {"item 1", "item 2 ", "list", "android", "item 3", "foobar", "bar", };
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_with_listview);
// implement your own adapter
}
}
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unneccessary calls
// to findViewById() on each row.
ViewHolder holder;
// When convertView is not null, we can reuse it directly, there is no need
// to reinflate it. We only inflate a new View when the convertView supplied
// by ListView is null.
if (convertView == null) {
convertView = mInflater.inflate(R.layout.temp, null);
// Creates a ViewHolder and store references to the two children views
// we want to bind data to.
holder = new ViewHolder();
holder.left = (TextView) convertView.findViewById(R.id.left);
holder.right = (TextView) convertView.findViewById(R.id.right);
convertView.setTag(holder);
} else {
// Get the ViewHolder back to get fast access to the TextView
// and the ImageView.
holder = (ViewHolder) convertView.getTag();
}
// Bind the data efficiently with the holder.
holder.left.setText("left text");
holder.right.setText("right text");
return convertView;
}
class ViewHolder
{
public TextView left;
public TextView right;
}
Each item of a ListView has to be a View. This includes ViewGroups. So you can use any Layout to arrange several Views inside a ListView item.