I am trying to develop a wallpaper application. When the application launches, an activity shows the categories (an image and corresponding name on a textView) on a gridView. When I select a category, it navigates to a sub-category. In sub-category activity, it also shows subcategory image with its corresponding name on textVew.
Problem is in sub-category activity images are not loading. Sometimes it loads a image; when i press back-button, then select that sub-category again, it loads three mode image; pressing back button and selecting than sub-category causes loading another three images (now its total 10 images).
The images is cached on the disk properly.
Here is my configuration:
cacheDir = new File(Environment.getExternalStorageDirectory(),
"peakwallpapers/cache"); // --
// Create configuration for ImageLoader
config = new ImageLoaderConfiguration.Builder(this).enableLogging()
.discCache(new UnlimitedDiscCache(cacheDir))
.memoryCache(new UsingFreqLimitedMemoryCache(2000000))
.denyCacheImageMultipleSizesInMemory().threadPoolSize(10)
.threadPriority(Thread.MIN_PRIORITY + 2)
.defaultDisplayImageOptions(DisplayImageOptions.createSimple())
.build(); // --
options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.stub_image)
.showImageForEmptyUri(R.drawable.image_for_empty_url)
.cacheInMemory().cacheOnDisc().build();
imageLoader.init(config);
Here is the getView() methood:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final View rowView;
final ViewHolder viewHolder = new ViewHolder();
if (convertView == null) {
rowView = getLayoutInflater().inflate(
R.layout.item_sub_category_grid, null);
viewHolder.imageView = (ImageView) rowView
.findViewById(R.id.image_item_sub_category);
viewHolder.textView = (TextView) rowView
.findViewById(R.id.text_item_sub_cat_desc);
rowView.setTag(viewHolder);
} else {
rowView = (View) convertView;
}
ViewHolder holder = (ViewHolder) rowView.getTag();
holder.textView.setText(wpSubCategories.get(position)
.getSubCategoryName());
imageLoader.displayImage(imageUrls[position], viewHolder.imageView,
options, new SimpleImageLoadingListener() {
#Override
public void onLoadingStarted() {
showLoading();
}
#Override
public void onLoadingComplete(Bitmap loadedImage) {
Animation anim = AnimationUtils.loadAnimation(
SubCategoryGridActivity.this,
R.anim.fade_in);
viewHolder.imageView.setAnimation(anim);
anim.start();
}
});
return rowView;
}
}
My Grid layout: sub_category_grid.xml :
<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/gridview_sub_category"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:horizontalSpacing="4dip"
android:numColumns="2"
android:verticalSpacing="4dip" />
and My item of grid: item_sub_category_grid.xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/frame_l_item_sub_category_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight=".5"
android:background="#drawable/textlines" >
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/image_item_sub_category"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:contentDescription="#string/descr_image"
android:scaleType="centerCrop" />
<TextView
android:id="#+id/text_item_sub_cat_desc"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="#drawable/cat_bg"
android:gravity="center_horizontal"
android:text="TextView"
android:textColorHighlight="#656565"
android:typeface="monospace" >
</TextView>
</FrameLayout>
N.B: It shows no error on LogCat.
First of all, I think your thread pool size is too big. Of course it depends on devices your app is targeted. But it will work really slow on not-modern devices. I think 5 is enough, but you should test it yourself. Vary thread pool size and thread priority to achieve best speed and performance.
Second: you have a logical mistake in your getView() method. Move
final ViewHolder viewHolder = new ViewHolder();
after
if (convertView == null) {
and you'll see the problem. Your vars viewHolder and holder logically overlaps each other.
UPD: Don't call ImageLoader.stop() in activity's onStop() if you do.
Related
This question already has answers here:
custom listview adapter getView method being called multiple times, and in no coherent order
(12 answers)
Closed 7 years ago.
This is my XML
<RelativeLayout
android:id="#+id/preview_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/socialbottom"
android:padding="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Preview"
android:textColor="#color/white"
android:textStyle="bold" />
<ImageButton
android:id="#+id/close_preview_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:background="#drawable/close_btn" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#+id/empty_view"
android:layout_below="#+id/preview_header">
<ListView
android:id="#+id/preview_dialog_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:divider="#android:color/transparent"
android:dividerHeight="8dp"
android:scrollbars="none"></ListView>
</RelativeLayout>
<View
android:id="#+id/empty_view"
style="#style/Space"
android:layout_width="match_parent"
android:layout_above="#+id/preview_add_more_btn"></View>
<Button
android:id="#+id/preview_add_more_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="#+id/empty_view1"
android:background="#color/content_text"
android:drawableLeft="#drawable/plus_icon"
android:drawablePadding="50dp"
android:gravity="center_horizontal"
android:padding="8dp"
android:paddingLeft="300dp"
android:text="#string/add_more"
android:textColor="#color/white" />
<View
android:id="#+id/empty_view1"
style="#style/Space"
android:layout_width="match_parent"
android:layout_above="#+id/footer_preview"></View>
<RelativeLayout
android:id="#+id/footer_preview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="vertical">
<EditText
android:id="#+id/preview_input_timeline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentStart="true"
android:background="#color/acticty_textbox"
android:hint="What's up, admin?"
android:inputType="textMultiLine"
android:padding="8dp" />
<Button
android:id="#+id/preview_post_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="#+id/preview_input_timeline"
android:layout_centerHorizontal="true"
android:layout_gravity="center_horizontal"
android:background="#drawable/savebox"
android:padding="8dp"
android:text="Post"
android:textAllCaps="true"
android:textColor="#color/white"
android:textStyle="bold" />
</RelativeLayout>
This is JAVA
public View getView(int position, View convertView, ViewGroup parent) {
convertView = mInflater.inflate(R.layout.preview_list_view, null);
ImageButton removeBtn = (ImageButton) convertView.findViewById(R.id.remove_preview);
ImageView imageView = (ImageView) convertView.findViewById(R.id.preview_image);
VideoView videoView = (VideoView) convertView.findViewById(R.id.preview_video);
Log.e("video sizzzessssssss", String.valueOf(imagesList.size()));
if (SocialActivity.MEDIA_TYPE_IMAGE == previewType) {
videoView.setVisibility(View.INVISIBLE);
imageView.setVisibility(View.VISIBLE);
String path = imagesList.get(position);
File imgFile = new File(path);
if (imgFile.exists()) {
// Bitmap myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
// imageView.setImageBitmap(myBitmap);
Bitmap d = new BitmapDrawable(context.getResources(), imgFile.getAbsolutePath()).getBitmap();
int nh = (int) (d.getHeight() * (512.0 / d.getWidth()));
Bitmap scaled = Bitmap.createScaledBitmap(d, 512, nh, true);
imageView.setImageBitmap(scaled);
}
} else {
imageView.setVisibility(View.INVISIBLE);
videoView.setVisibility(View.VISIBLE);
videoView.setVideoPath(imagesList.get(position));
MediaController mediaControls = new MediaController(SocialActivity.socialActivity);
videoView.setMediaController(mediaControls);
videoView.start();
videoView.pause();
Log.e("path video", imagesList.get(position));
}
removeBtn.setOnClickListener(new ListCustomClickEvents(callback, position));
return convertView;
}
You have to use the setTag() and getTag()
like these
public class ViewHolder {
//Declare yours all component here
// like below example
private ImageView profile_iv;
private TextView name_tv;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
///Firstly check the convertView is null or not like below
final ViewHolder _viewHolder;
if (convertView == null) {
_viewHolder = new ViewHolder();
LayoutInflater _layInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = _layInflater.inflate(R.layout.connection_friend_item, null);
///Finds yours layout items id here like below example
_viewHolder.profile_iv = (ImageView) convertView.findViewById(R.id.profile_iv);
_viewHolder.name_tv = (TextView) convertView.findViewById(R.id.name_tv);
convertView.setTag(_viewHolder);
} else {
_viewHolder = (ViewHolder) convertView.getTag();
}
////Set the data in the layout's item as below
_viewHolder.name_tv.setText(imagesList.get(position));
_viewHolder.profile_iv.setImageBitmap(imagesList.get(position));
return convertView;
}
I think you should use View Holder design pattern enables you to access each list item view without the need for the look up, saving valuable processor cycles. Specifically, it avoids frequent call of findViewById() during ListView scrolling, and that will make it smooth.
Refer below code for Demo:
public static class ViewHolder{
public TextView aliasTextView;
public TextView numTextView;
public TextView statusTextView;
public RelativeLayout mainLayout;
public NetworkImageView _profileImageView;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
final ViewHolder holder;
LayoutInflater inflater = (LayoutInflater)_context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
DisputeListBean bean = _ArrayList.get(position);
if(convertView == null)
{
holder = new ViewHolder();
v = inflater.inflate(R.layout.dispute_row, null,false);
holder.mainLayout = (RelativeLayout)v.findViewById(R.id.mainLayout);
holder.aliasTextView = (TextView)v.findViewById(R.id.user_name);
holder.statusTextView = (TextView)v.findViewById(R.id.user_status);
holder._profileImageView = (NetworkImageView)v.findViewById(R.id.profile_pic);
v.setTag(holder);
}else {
holder = (ViewHolder) v.getTag();
}
holder.aliasTextView.setText(bean.getAlias_name().toUpperCase());
return v;
}
Don't give listview's height as wrap_content. When you the height as wrap_content, the listview will first populate few list items to determine the actual height of the listview
Due to inner caching mechanism in ListView, Android will be fire two times getView method for each visible item. It happens only for first time, when you open Activity/Fragment/View that contains a ListView. For more information refer to google with ScrapViews ListView request.
To solve your problem you need just check
if(convertView == null)
convertView = inflate...
or use ViewHolder pattern from Google.
I've set up a grid of ImageViews with TextView overlays. My ImageAdapter code is as follows:
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
View grid;
LayoutInflater inflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
grid = new View(mContext);
grid = inflater.inflate(R.layout.image, null);
TextView textView = (TextView)grid.findViewById(R.id.mastery_text);
imageView = (ImageView)grid.findViewById(R.id.mastery_image);
grid.setLayoutParams(new GridView.LayoutParams(150, 150));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
grid.setPadding(8, 8, 8, 8);
grid.setBackgroundResource(R.color.orange);
imageView.setImageResource(mThumbIds[position]);
} else {
grid = (View) convertView;
}
return grid;
}
The corresponding XML layout for my ImageAdapter is this:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layout_practitioner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ImageView
android:id="#+id/mastery_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="bottom"
android:background="#bbffffff"
android:focusable="false"
android:focusableInTouchMode="false" >
<TextView android:id="#+id/mastery_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:textColor="#color/black"
android:gravity="bottom|center"
android:textSize="12sp"
android:textAllCaps="true"
android:paddingBottom="0dp"
android:text="3/3"
/>
</LinearLayout>
</FrameLayout>
XML code for my GridView (activity class):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:id="#+id/gridview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:numColumns="4"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:stretchMode="columnWidth"
android:gravity="center" />
</LinearLayout>
Here is the onCreate method of my main activity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_masteries);
GridView gridview = (GridView) findViewById(R.id.gridview);
ImageAdapter adapter = new ImageAdapter(this);
gridview.setAdapter(adapter);
gridview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,
int position, long id) {
Toast.makeText(Masteries.this, "" + position,
Toast.LENGTH_SHORT).show();
}
});
}
I want to, after initializing the grid with the images, change one of the ImageViews from my activity class, given its position on the grid. How would I do that?
I'm not asking to change the image in response to an onItemClick.
Thanks in advance!
Edit:
I'm thinking of creating a changeImage(int position, int imageId) method in my Adapter and calling that from my activity class. Is that the right approach?
In your adapter:
public void updateImage(int position, int resourceId)
{
mThumbIds[position] = resourceId;
notifyDataSetChanged();
}
In your activity:
mAdapter.updateImage(<position>, <image_resource_id>);
Notes
You will have to make the adapter a member of your activity
The main idea is that you modify the backing data and and notify the GridView that this has changed and it is time to be redrawn
Your getView() method implementation needs a lot of improvement. It will cause lots of bugs once the system starts recycling the views (the convertView parameter comes in != null for a position different than it was used for last time)
Here's a sketch of how your getView() should look like:
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder viewHolder;
LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
convertView = inflater.inflate(R.layout.image, parent, false);
// next three lines would not be necessary if:
// a) it is the same for every item;
// b) you inflate properly (using the parent);
// c) you specify this in the item's xml (R.layout.image)
convertView.setLayoutParams(new GridView.LayoutParams(150, 150));
convertView.setPadding(8, 8, 8, 8);
convertView.setBackgroundResource(android.R.color.holo_red_light);
viewHolder = new ViewHolder();
viewHolder.mTextView = (TextView)convertView.findViewById(R.id.mastery_text);
viewHolder.mImageView = (ImageView)convertView.findViewById(R.id.mastery_image);
// this could also be set in xml perhaps
viewHolder.mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder)convertView.getTag();
}
// update the values every time we are being asked to update the item,
// because the item might have been reused from a different position
viewHolder.mImageView.setImageResource(mThumbIds[position]);
//viewHolder.mTextView.setText("myText");
return convertView;
}
public static class ViewHolder
{
TextView mTextView;
ImageView mImageView;
}
You can set a unique id to each ImageView using names (See here) or even integer numbers with a sequence or something (increasing at each image you put in the view).
Another good way to do this is using the tags, with setTag() and getTag().
This question has a good answer: What is the main purpose of setTag() getTag() methods of View?
I'm learning Android, and following this tutorial on custom ListView Items.
However, I've created my own ListView item and when I load up the app (on my Galaxy S4, physical device) it becomes incredibly slow.
When I use a simple_list_item_1 for my listview, everything runs smooth, but when I use my own custom item it runs super slow. I can't find out why this is. There seem to be no expensive (and definitely not infinitely running) operations that I created.
I've also noticed that even tho I have only 5 listItems, the getView method gets called around 15 times. An explanation to why this is would also be welcome. (They might be related)
For my Activity I used Android Studio (1.2.2) standard "Navigation Drawer Activity". I've only been adding stuff in the onCreateView method. Which now looks like this:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
/* Start of my custom code */
//Create some list items
String[] words = {"Defenestration", "Indicative", "Executive", "Developmental", "Consciousness"};
//The list in the Fragment
ListView list = (ListView) rootView.findViewById(R.id.mainList);
//The custom ListAdapter
ListAdapter la = new ShaggyAdapter(getActivity(), words);
//A built in listadapter for testing
//ListAdapter la2 = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, words);
list.setAdapter(la);
//Create listener
list.setOnItemClickListener(
new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String word = String.valueOf(parent.getItemAtPosition(position));
Toast.makeText(getActivity(), word, Toast.LENGTH_SHORT).show();
}
});
/* End of my custom code */
return rootView;
}
The custom adapter looks like this:
class ShaggyAdapter extends ArrayAdapter<String>{
private static final String TAG = "coo";
public ShaggyAdapter(Context context, String[] words) {
super(context, R.layout.shaggy_item, words);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(getContext());
if (convertView == null){
convertView = inflater.inflate(R.layout.shaggy_item, parent, false);
Log.i(TAG, "inflate");
}else{
Log.i(TAG, "Don't inflate");
}
String word = getItem(position);
TextView name = (TextView) convertView.findViewById(R.id.itemName);
name.setText(word);
return convertView;
}
}
The custom List Item looks like this:
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:columnCount="5">
<ImageView
android:layout_width="80dp"
android:layout_height="80dp"
android:id="#+id/itemImage"
android:layout_row="0"
android:layout_column="0"
android:src="#drawable/no_profile"
android:layout_margin="8dp"
android:layout_rowSpan="2"
android:contentDescription="#string/shaggy_item_image_description" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="#string/shaggy_item_name_placeholder"
android:id="#+id/itemName"
android:layout_row="0"
android:layout_column="1"
android:layout_margin="8dp"
android:layout_marginTop="14dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="#string/shaggy_item_new_tag"
android:id="#+id/itemNew"
android:layout_row="0"
android:layout_column="2"
android:layout_marginTop="14dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="#string/shaggy_item_date_placeholder"
android:id="#+id/itemDate"
android:layout_row="1"
android:layout_column="1"
android:layout_margin="8dp"
android:layout_columnSpan="2" />
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:id="#+id/itemStar"
android:layout_row="0"
android:layout_column="3"
android:src="#drawable/rating_star_1"
android:layout_margin="8dp"
android:layout_marginTop="14dp"
android:layout_rowSpan="2"
android:contentDescription="#string/shaggy_item_star_description" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="#string/shaggy_item_rating_placheholder"
android:id="#+id/itemRating"
android:layout_row="0"
android:layout_column="4"
android:layout_margin="8dp"
android:layout_marginTop="14dp"
android:layout_rowSpan="2" />
</GridLayout>
Any suggestions would be highly appreciated.
I found the answer, I made a very silly mistake. The image I was using in the profile image view was 2000x2000px and I displayed it in a 80x80dp imageview. I noticed memory usage suddenly doubled.
Using a smaller image (currently 300x300px) made everything run super smooth. What I've learned today:
- Use correctly sized images! Android doesn't like handling images.
I will also be using the Holding Pattern as suggested by Boss and King of Masses to make it extra smooth.
why findViewById is so slow? And why View Holder
Pattern is faster?
When you are not using Holder so getView() method will call findViewById() as many times as you row(s) will be out of View. So if you have 1000 rows in List and 990 rows will be out of View then 990 times will be called findViewById() again.
Holder design pattern is used for View caching - Holder (arbitrary) object holds child widgets of each row and when row is out of View then findViewById() won't be called but View will be recycled and widgets will be obtained from Holder.
if (convertView == null) {
convertView = inflater.inflate(layout, null, false);
holder = new Holder(convertView);
convertView.setTag(holder); // setting Holder as arbitrary object for row
}
else { // view recycling
// row already contains Holder object
holder = (Holder) convertView.getTag();
}
// set up row data from holder
titleText.setText(holder.getTitle().getText().toString());
Where Holder class can looks like:
public class Holder {
private View row;
private TextView title;
public Holder(View row) {
this.row = row;
}
public TextView getTitle() {
if (title == null) {
title = (TextView) row.findViewById(R.id.title);
}
return title;
}
}
Here is second approach how to use ViewHolder pattern:
ViewHolder holder;
// view is creating
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.row, parent, false);
holder = new ViewHolder();
holder.title = (TextView) convertView.findViewById(R.id.title);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
}
// view is recycling
else {
holder = (ViewHolder) convertView.getTag();
}
// set-up row
final MyItem item = mItems.get(position);
holder.title.setText(item.getTitle());
...
private static class ViewHolder {
public TextView title;
public ImageView icon;
}
This Android listview using ViewHolder will help you to implement the same.
As everybody know, Google and AppCompat v7 as support library released new ViewGroup called RecyclerView that is designed for rendering any adapter-based views.
Cheers !!
In a fragment, I have a ListView that has a custom ParseQueryAdapter<T>. The problem may not have anything to do with Parse, although I'm not sure.
As I was testing my app, I noticed something very strange. When I would scroll down my ListView, all the visible ListView items would be drawn on top of the next ListView item as seen in the second image below.
The list initialized properly as such:
As you can see, in my list item layout, I have an ImageView (ParseImageView to be specific) and a TextView. The TextView now displays some notes (don't mind the ID user_name_text_view) and the ImageView displays a placeholder blank profile picture.
When I scrolled down, the list looked like:
Here's my list view layout named fragment_post_view_list_view:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="#+id/post_list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
</LinearLayout>
Here's my list item layout named list_item_post_view:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.parse.ParseImageView
android:id="#+id/icon"
android:layout_width="75dp"
android:layout_height="75dp"
android:layout_alignParentLeft="true"
android:background="#drawable/com_facebook_profile_picture_blank_square" />
<TextView
android:id="#+id/user_name_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/icon"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="#color/link_blue" />
</RelativeLayout>
Here's my adapter named PostViewListViewAdapter:
public class PostViewListViewAdapter extends ParseQueryAdapter<Post> {
// call superclass with a query to populate list view
public PostViewListViewAdapter(Context context, final String[] postsObjectIds) {
super(context, new ParseQueryAdapter.QueryFactory<Post>(){
public ParseQuery<Post> create() {
ParseQuery<Post> query = Post.getQuery();
query.whereContainedIn("objectId", Arrays.asList(postsObjectIds));
return query;
}
});
}
// this is similar to getView method in an adapter
#Override
public View getItemView(Post post, View v, ViewGroup parent) {
if(v == null) {
v = View.inflate(getContext(), R.layout.list_item_post_view, null);
}
super.getItemView(post, v, parent);
TextView usernameTextView = (TextView) v.findViewById(R.id.user_name_text_view);
usernameTextView.setText(post.getNotes()); // some string
return v;
}
}
How can I fix this problem?
Is this an issue with XML or Java?
I was following the two tutorials from Parse and the example from the Parse docs:
MealSpotting
Parse Query Adapter
I set the adapter and ListView here:
View rootView = inflater.inflate(R.layout.fragment_post_view_list_view, container, false);
mPostsObjectIds = SOME_STRING[];
PostViewListViewAdapter adapter = new PostViewListViewAdapter(getActivity(), mPostsObjectIds);
ListView listView = (ListView) rootView.findViewById(R.id.post_list_view);
listView.setAdapter(adapter);
I've tried getting rid of the ParseImageView in my list item layout, but my TextViews still draw on top of each other when I scroll.
Edit:
I forgot to mention that the list items display on top of each other after an orientation change.
I tested this on my Galaxy S5 (Android version 4.4.2 and Parse 1.4.1).
In my Activity, I show the Fragment here (called PostViewListViewFragment):
getSupportFragmentManager().beginTransaction().add(android.R.id.content, new PostViewListViewFragment()).commit();
Try below layout :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="#+id/post_list_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="none" >
</ListView>
</RelativeLayout >
Make Sure your adapter like this:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View rowView = convertView;
// reuse views
if (rowView == null) {
LayoutInflater inflater = context.getLayoutInflater();
rowView = inflater.inflate(R.layout.rowlayout, null);
// configure view holder
ViewHolder viewHolder = new ViewHolder();
viewHolder.text = (TextView) rowView.findViewById(R.id.TextView01);
rowView.setTag(viewHolder);
}
// fill data
ViewHolder holder = (ViewHolder) rowView.getTag();
String s = names[position];
holder.text.setText(s);
return rowView;
}
}
PS:You should watch this Google IO video about Listview,and here is the slides.
First create a ViewHolder class
static class ViewHolder {
protected TextView usernameTextView;
}
Then change your getItemView method like below
public View getItemView (Post post, View convertView , ViewGroup parent)
{
ViewHolder viewHolder = null;
if (convertView == null)
{
LayoutInflater inflator = context.getLayoutInflater();
convertView = inflator.inflate(R.layout.list_item_post_view, null);
viewHolder = new ViewHolder();
viewHolder.usernameTextView = (TextView)convertView.findViewById(R.id.user_name_text_view);
convertView.setTag(viewHolder);
convertView.setTag(R.id.user_name_text_view, viewHolder.usernameTextView);
}
else
{
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.usernameTextView.setText(post.getNotes()); // some string
return convertView;
}
The problem seems to be in your list item layout -
Change it to this -
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.parse.ParseImageView
android:id="#+id/icon"
android:layout_width="75dp"
android:layout_height="75dp"
android:layout_alignParentLeft="true" />
<TextView
android:id="#+id/user_name_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/icon"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="#color/link_blue" />
</RelativeLayout>
Probably you have extra background for each list item set that is causing such effect.
Alter and watch.
Hope this gives you idea!
Try changing your list view layout height to match_parent.
Credit to #VedPrakash for helping me fix this.
In case it helps anyone, I fixed the problem by replacing the fragment not adding it. So I changed this line from:
getSupportFragmentManager().beginTransaction().add(android.R.id.content, new PostViewListViewFragment()).commit();
to:
getSupportFragmentManager().beginTransaction().replace(android.R.id.content, new PostViewListViewFragment()).commit();
I am trying to add an image to my ListView to make it look more like a button. I would like the images to be a little smaller, maybe 60% of current. And the images to lign up nicely on the right in a column. Here is a screen of what I currently have:
and here is my list view xml:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:padding="10dp"
android:textSize="16sp"
android:layout_width="match_parent"
android:drawableRight="#drawable/arrow_button"
>
</TextView>
any idea what I am doing incorrectly?
The ListView that contains this TextView is defined like this:
One note, the way I create and work with my Lists is with the ListAdapter, using code like this:
Question q = new Question ();
q.setQuestion( "This is a test question and there are more than one" );
questions.add(q);
adapter = new ArrayAdapter<Question>( this, R.layout.questions_list, questions);
setListAdapter(adapter);
Thanks!
Ahh. You are doing the correct thing using a compound drawable. Not sure if there is a better way to maybe have the spacing in your compound drawable expand, but I know this'll work.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:padding="10dp"
android:textSize="16sp"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true" />
<View
android:layout_height="64dip"
android:layout_width="64dip"
android:background="#drawable/arrow_button"
android:layout_centerVertical="true"
android:layout_alignParentRight="true" />
</RelativeLayout>
Basically just pointing out using the align parent right and left. You may want to add some margins or padding to them. Also make sure to vertically center your elements.
With the comment and advice that Frank Sposaro gave, you will be able to position your views correctly.
For your next problem, I advice you to make your own adapter similar to this:
private class CustomAdapter extends ArrayAdapter<Question> {
private LayoutInflater mInflater;
public CustomAdapter(Context context) {
super(context, R.layout.row);
mInflater = LayoutInflater.from(context);
}
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.row, null);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.mTextView);
holder.image = (ImageView) convertView.findViewById(R.id.mImage);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
//Fill the views in your row
holder.text.setText(questions.get(position).getText());
holder.image.setBackground... (questions.get(position).getImage()));
return convertView;
}
}
static class ViewHolder {
TextView text;
ImageView image;
}
In your onCreate:
ListView mListView = (ListView) findViewById(R.id.mListView);
mListView.setAdapter(new CustomAdapter(getApplicationContext(), questions));
Another example for a ListView with an Adapter can be found here