Not exactly sure what is going on here so I am hoping one of you can point me in the right direction. I have a ListView within a Tab frame and for some reason, it loads but crashes when I try to scroll.
Here is all of the information I could gather
Here is the Activity that goes into the frame... it parses an XML file into a priority queue, gets the attached intent information, and loads the desired page.
public class QScoresFrameActivity extends ListActivity
{
private PriorityQueue<Score> mScores;
private int mDifficulty;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.scoresframe);
mDifficulty = getIntent().getIntExtra(getResources().getString(R.string.difficulty), getResources().getInteger(R.integer.normal_level));
mScores = QGameGlobals.ParseScoresXML(this,mDifficulty);
setListAdapter(new ScoresAdapter(this));
}
private class ScoresAdapter extends BaseAdapter
{
private LayoutInflater mInflater;
public ScoresAdapter(Context context)
{
mInflater = LayoutInflater.from(context);
}
public View getView(int position, View convertView, ViewGroup parent)
{
Score thisScore = mScores.getByIndex(position);
convertView = mInflater.inflate(R.layout.scoreview, null);
TextView posT;
TextView nameT;
TextView scoreT;
String name = thisScore.getName();
int score = thisScore.getScore();
posT = (TextView)convertView.findViewById(R.id.position);
nameT = (TextView)convertView.findViewById(R.id.scorerName);
scoreT= (TextView)convertView.findViewById(R.id.scorerScore);
posT.setText(String.valueOf(position + 1) + ". ");
nameT.setText(name);
scoreT.setText(String.valueOf(score));
return convertView;
}
public int getCount()
{
return mScores.getLength();
}
public Object getItem(int position)
{
return this;
}
public long getItemId(int position)
{
return position;
}
}
}
The layout file that holds the list:
<?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="fill_parent" android:orientation="vertical">
<ListView android:layout_height="wrap_content" android:layout_width="match_parent" android:id="#+android:id/list" />
</LinearLayout>
The layout file for each item in the list:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="#+id/linearLayout1" android:padding="10dp" android:layout_width="fill_parent" android:baselineAligned="false" android:orientation="horizontal" android:layout_height="wrap_content">
<TextView android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="TextView" android:paddingRight="10dp" android:id="#+id/position" android:layout_width="50dp" android:textColor="#color/teal" ></TextView>
<TextView android:id="#+id/scorerName" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:text="TextView" android:layout_width="wrap_content" android:layout_gravity="center_vertical"></TextView>
<TextView android:id="#+id/scorerScore" android:text="TextView" android:textAppearance="?android:attr/textAppearanceLarge" android:paddingLeft="20dp" android:layout_height="wrap_content" android:gravity="right" android:textColor="#color/gold" android:layout_width="match_parent" android:layout_gravity="center_vertical"/>
</LinearLayout>
Priority Queue class....
#SuppressWarnings("rawtypes")
public class PriorityQueue<T extends Comparable> extends Queue<T>
{
public PriorityQueue ()
{
super();
}
#SuppressWarnings("unchecked")
#Override
public void enqueue(T item)
/* Enqueue a new object of type T into the queue based on its compareTo method.
* Sorts from greatest to least.
*/
{
TNode newNode = new TNode(item);
if (root == null)
{
root = newNode;
end = newNode;
}
else
{
TNode currNode = root;
TNode lastNode = root;
while (true)
{
int compVal = item.compareTo(currNode.get());
if (compVal == 1 || compVal == 0)
{
if (currNode == root)
{
root = newNode;
}
else
{
lastNode.next = newNode;
newNode.next = currNode;
}
newNode.next = currNode;
break;
}
else
{
if (currNode == end)
{
currNode.next = newNode;
end = newNode;
break;
}
else
{
lastNode = currNode;
currNode = currNode.next;
}
}
}
}
length++;
}
}
Appreciate the help.
A stack trace here would be most helpful. However, offhand, I would guess that the problem is you are storing the inflator in the adapter's constructor. Instead, just call getLayoutInflator() inside your getView() method. If that doesn't resolve it, please do post the logcat.
Just gonna guess because I ran into this when I started out with Android...
I know the documentation says to name your list android:id="#+android:id/list" but try android:id="#id/android:list" instead also if it just so happens that you're starting with no info (empty list) define a view with android:id="#id/android:empty" as a placeholder.
The problem is with your getView() function in your custom listadapter (ScoreAdapter). When you scroll, the listview recycles your views and calls the getView() function. Change you getView() function to this to fix it:
public View getView(int position, View convertView, ViewGroup parent)
{
Score thisScore = mScores.getByIndex(position);
if (convertView == null)
{
LayoutInflater mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.scoreview, null);
}
TextView posT;
TextView nameT;
TextView scoreT;
String name = thisScore.getName();
int score = thisScore.getScore();
posT = (TextView)convertView.findViewById(R.id.position);
nameT = (TextView)convertView.findViewById(R.id.scorerName);
scoreT= (TextView)convertView.findViewById(R.id.scorerScore);
posT.setText(String.valueOf(position + 1) + ". ");
nameT.setText(name);
scoreT.setText(String.valueOf(score));
return convertView;
}
Ahh yes, your problem is definitely in the PriorityQueue. That enqueue method is all sorts of broken, sorry. Get out a pencil and paper and walk through the scenarios of adding in the middle of the queue. I'm pretty certain that I see at least one bug, and possibly as many as three.
--randy
Related
I am trying to inflate listView with two different layouts using ArrayAdapter.
I want to show some user posts. If it contains a picture use one layout if it does not use second.
I am doing so far
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Post post = getItem(position);
boolean noImg = post.mImgUrl1 == null && post.mImgUrl2 == null;
if (convertView == null){
if (noImg)
convertView = LayoutInflater.from(getContext()).inflate(R.layout.post_item_no_img, parent ,false);
else
convertView = LayoutInflater.from(getContext()).inflate(R.layout.post_item, parent, false);
}
if (noImg == false) {
mTitle = (TextView) convertView.findViewById(R.id.tv_titlePost);
mText = (TextView) convertView.findViewById(R.id.tv_textPost);
mImage = (ImageView) convertView.findViewById(R.id.iv_postimg);
}
else {
mNoImgTitle = (TextView) convertView.findViewById(R.id.tv_noImgTitle);
mNoImgText = (TextView) convertView.findViewById(R.id.tv_noImgText);
}
if (noImg) {
mNoImgTitle.setText(post.mTitle);
mNoImgText.setText(post.mText);
if (post.mImgUrl1 != null)
Glide.with(mImage.getContext()).load(post.mImgUrl1).into(mImage);
else Glide.with(mImage.getContext()).load(post.mImgUrl2).into(mImage);
}
else {
mNoImgTitle.setText(post.mTitle);
mNoImgText.setText(post.mText);
}
return convertView;
}
But i realized that textviews from second layout cannot be found by findViewById function. And it always returns a null-pointer.
What are my solutions ? Should i make only one Layout and somehow trick it using visibility ? Or there is a way ?
There are a few different scenarios that you will see when getView() is called:
convertView is null: In this case just create the layout that you need depending upon noImg;
convertView is defined but is of the wrong type. In this case, you will need to discard the old view and create the one you need;
convertView is defined but is of the correct type, in which case, we do nothing with it.
The following code is one approach to making sure that you are always working with the right layout and can find the ids that you expect. This code uses a tag in the inflated layout to identify the layout type that can be checked.
if (convertView == null || noImg != (boolean) convertView.getTag()) {
// Either there is no view defined or it is the wrong type. Create
// the type we need.
if (noImg) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.post_item_no_img, parent, false);
} else {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.post_item, parent, false);
}
convertView.setTag(noImg);
}
With BaseAdapter you can create more complex layouts. Try using it.
Here is the example of use for your situation:
Activity:
public class TestActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String[] data={"false","false","true","true","false","true"};
ListView listViewTest=(ListView)findViewById(R.id.listViewTest);
TestAdapter testAdapter=new TestAdapter(data);
listViewTest.setAdapter(testAdapter);
}
}
Adapter:
public class TestAdapter extends BaseAdapter {
private String[] data;
TestAdapter(String data[]) {
this.data = data;
}
#Override
public int getCount() {
return data.length;
}
#Override
public Object getItem(int i) {
return data[i];
}
#Override
public long getItemId(int i) {
return i;
}
public View getView(int position, View convertView, ViewGroup parent) {
if (data[position].equals("false")) {
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_no_img, parent, false);
} else if (data[position].equals("true")) {
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_img, parent, false);
}
return convertView;
}
}
Layouts:
activity_main
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ListView
android:id="#+id/listViewTest"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
list_item_no_img
<?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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/textViewTest"
android:gravity="center"
android:layout_centerInParent="true"
android:textSize="32sp"
android:text="Test"/>
</RelativeLayout>
list_item_img
<?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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/textViewTest"
android:gravity="center"
android:layout_centerInParent="true"
android:textSize="32sp"
android:text="Test"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#mipmap/ic_launcher"
android:layout_marginStart="5dp"
android:layout_marginLeft="5dp"
android:layout_centerVertical="true"
android:layout_toRightOf="#+id/textViewTest"
android:layout_toEndOf="#+id/textViewTest" />
</RelativeLayout>
Result:
I have found similar questions, but can't find a specific answer that is up-to-date.
I'm using <preference-header>, as per 3.0+ settings design guidelines ( I target 4.1.2+) to build my headers; I want to set a custom layout to these headers. Note that I don't want to fall back to the old PreferenceScreen method as described here, because I don't support older Android version.
As far as I could research, this layout is held by a private member of the PreferenceActivity class, and it's retrieved with a styleable attribute that doesn't seem publicly accessible:
private int mPreferenceHeaderItemResId = 0;
...
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
...
TypedArray sa = obtainStyledAttributes(null,
com.android.internal.R.styleable.PreferenceActivity,
com.android.internal.R.attr.preferenceActivityStyle,
0);
...
mPreferenceHeaderItemResId = sa.getResourceId(
com.android.internal.R.styleable.PreferenceActivity_headerLayout,
com.android.internal.R.layout.preference_header_item);
...
}
This resource is then passed to a private Adapter to populate the header ListView.
Is there a way to pass a different layout resource?
UPDATE 14.4.2016: there was problem with recreate by savedInstanceState, but I found another similar solution from which I used setListAdapter method code (I modified the code below).
I solve this problem just too. I don't know if the following solution is correct, but was the fastest. Because PreferenceActivity is child of ListActivity, you can override setListAdapter method for using own adapter for header items. This is ugly hack, because setListAdapter method is called in PreferenceActivity.onCreate() with sets adapter parameter to new instance of HeaderAdapter, so following adjustment ignore this instance.
#Override
public void setListAdapter(ListAdapter adapter) {
int i, count;
if (mHeaders == null) {
mHeaders = new ArrayList<>();
// When the saved state provides the list of headers, onBuildHeaders is not called
// so we build it from the adapter given, then use our own adapter
count = adapter.getCount();
for (i = 0; i < count; ++i) {
mHeaders.add((Header) adapter.getItem(i));
}
}
super.setListAdapter(new CustomHeaderAdapter(this, mHeaders, R.layout.preference_header_item, true));
}
mHeaders property is defined as class member
private List<Header> mHeaders;
and is assigned in onBuildHeaders:
#Override
public void onBuildHeaders(List<Header> target) {
mHeaders = target;
loadHeadersFromResource(R.xml.preference_headers, target);
...
}
I copied and modified adapter inner class and layout from SDK source:
private static class CustomHeaderAdapter extends ArrayAdapter<Header> {
private static class HeaderViewHolder {
ImageView icon;
TextView title;
TextView summary;
}
private LayoutInflater mInflater;
private int mLayoutResId;
private boolean mRemoveIconIfEmpty;
public CustomHeaderAdapter(Context context, List<Header> objects, int layoutResId,
boolean removeIconBehavior) {
super(context, 0, objects);
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mLayoutResId = layoutResId;
mRemoveIconIfEmpty = removeIconBehavior;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
HeaderViewHolder holder;
View view;
if (convertView == null) {
view = mInflater.inflate(mLayoutResId, parent, false);
holder = new HeaderViewHolder();
holder.icon = (ImageView) view.findViewById(R.id.icon);
holder.title = (TextView) view.findViewById(R.id.title);
holder.summary = (TextView) view.findViewById(R.id.summary);
view.setTag(holder);
} else {
view = convertView;
holder = (HeaderViewHolder) view.getTag();
}
// All view fields must be updated every time, because the view may be recycled
Header header = getItem(position);
if (mRemoveIconIfEmpty) {
if (header.iconRes == 0) {
holder.icon.setVisibility(View.GONE);
} else {
holder.icon.setVisibility(View.VISIBLE);
holder.icon.setImageResource(header.iconRes);
}
} else {
holder.icon.setImageResource(header.iconRes);
}
holder.title.setText(header.getTitle(getContext().getResources()));
CharSequence summary = header.getSummary(getContext().getResources());
if (!TextUtils.isEmpty(summary)) {
holder.summary.setVisibility(View.VISIBLE);
holder.summary.setText(summary);
} else {
holder.summary.setVisibility(View.GONE);
}
return view;
}
}
preference_header_item.xml with modified minHeight:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="64dp"
android:background="?android:attr/activatedBackgroundIndicator"
android:gravity="center_vertical"
android:paddingRight="?android:attr/scrollbarSize"
android:paddingEnd="?android:attr/scrollbarSize">
<ImageView
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dip"
android:layout_marginEnd="6dip"
android:layout_gravity="center" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="2dip"
android:layout_marginStart="2dip"
android:layout_marginRight="6dip"
android:layout_marginEnd="6dip"
android:layout_marginTop="6dip"
android:layout_marginBottom="6dip"
android:layout_weight="1">
<TextView android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
<TextView android:id="#+id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#android:id/title"
android:layout_alignLeft="#android:id/title"
android:layout_alignStart="#android:id/title"
android:textAppearance="?android:attr/textAppearanceSmall"
android:ellipsize="end"
android:maxLines="2" />
</RelativeLayout>
</LinearLayout>
I want to display a ProgressBar in each view of my ListView.
I have created a holder class which extends AsyncTask for the purpose of updating the progress bar in the view.
My current implementation work unless I try to scroll the ListView. After scrolling the wrong progress bars are updated and some of the previously finished progress bars are started from the position of whichever is currently running.
I am extending BaseAdapter, my getView method is as follows:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (convertView == null) {
LayoutInflater li = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = li.inflate(R.layout.list_item, null);
}
ProgressBarTask asyncViewHolder = mList.get(position);
if(asyncViewHolder != null){
asyncViewHolder.setViews(view);
if (asyncViewHolder.getStatus() == AsyncTask.Status.PENDING) {
asyncViewHolder.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
return view;
}
My Async holder class is as follows:
public class ProgressBarTask extends AsyncTask<Void, Integer, Void> {
private static final String TAG = ProgressBarTask.class.getName().toString();
private String title;
public TextView mProgressTitle;
public ProgressBar mProgressBar;
public TextView mProgressPercent;
public ProgressBarTask(String text){
title = text;
}
public void setViews(View base){
mProgressTitle = (TextView) base.findViewById(R.id.progress_title);
mProgressBar = (ProgressBar) base.findViewById(R.id.progress_horizontal);
mProgressPercent = (TextView) base.findViewById(R.id.progress_percent);
#Override
protected Void doInBackground(Void... ignore) {
for (int i = 0; !isCancelled() && i < 100; i++) {
if(i%5 == 0){
Log.i(TAG, title + " publishProgress(" + i + "%)");
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
publishProgress(i);
}
return null;
}
#Override
protected void onProgressUpdate(Integer... percent) {
if(percent[0]%5 == 0){
Log.i(TAG, title +" onProgressUpdate(" + percent[0] + "%)");
}
mProgressBar.setProgress(percent[0] * mProgressBar.getMax() / 100);
mProgressPercent.setText(percent[0] + "%");
}
...
...
}
And my list_item is described as follows:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/list_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/progress_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:paddingLeft="8dp"
android:paddingTop="8dp"
android:text="Progress:"
android:textAppearance="?android:attr/textAppearanceLarge" />
<ProgressBar
android:id="#+id/progress_horizontal"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:max="213"
android:padding="8dp" />
<TextView
android:id="#+id/progress_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingBottom="8dp"
android:text="0%"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
I have tried a lot of different ways to make this work. Can someone please explain to me what I am doing wrong?
Thank you.
The problem is when you scroll, what was at position 5, is now --for instance-- at 1 now.
Your code then says mList.get(1), when the ProgressBarTask that you're actually looking for is mList.get(5), because it was created at position 5 when stored in mList.
In a nutshell, position only corresponds to the correct index for mList when there is less than one page of content. As soon as you scroll, the indexes become "out of sync".
Consider using an ArrayAdapter to make your life easier.
Like the title say, i create list view which each item display image, three line text, and a button, but the view is so lagging even in samsung galaxy mega dual core, here my item view xml
<RelativeLayout
android:gravity="center_vertical"
android:padding="5.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:minHeight="?android:listPreferredItemHeight"
xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:id="#+id/compositeListViewItemThumbnailImage"
android:layout_width="60.0dip"
android:layout_height="60.0dip"
android:src="#drawable/ic_launcher"
android:scaleType="fitCenter"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_alignWithParentIfMissing="true"
android:contentDescription="#string/_empty_strings" />
<LinearLayout
android:orientation="vertical"
android:id="#+id/compositeListViewItemFirstLinearLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/compositeListViewItemThumbnailImage"
android:layout_alignTop="#+id/compositeListViewItemThumbnailImage"
android:layout_alignBottom="#+id/compositeListViewItemThumbnailImage"
android:layout_alignParentRight="true">
<TextView
android:textSize="16.0sp"
android:gravity="center_vertical"
android:id="#+id/compositeListViewItemFirstLineText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello_world"
android:singleLine="true"
android:ellipsize="marquee"
android:lines="1"
android:inputType="textNoSuggestions" />
<TextView
android:textSize="12.0sp"
android:id="#+id/compositeListViewItemSecondLineText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello_world"
android:singleLine="true"
android:ellipsize="marquee"
android:lines="1"
android:inputType="textNoSuggestions" />
<TextView
android:textSize="14.0sp"
android:singleLine="true"
android:ellipsize="marquee"
android:lines="1"
android:layout_gravity="center_vertical"
android:id="#+id/compositeListViewItemThirdLineText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello_world"
android:inputType="textNoSuggestions" />
</LinearLayout>
<Button
android:id="#+id/compositeListViewItemActionButton"
android:paddingLeft="5.0dip"
android:paddingTop="5.0dip"
android:paddingRight="5.0dip"
android:paddingBottom="5.0dip"
android:focusable="false"
android:layout_width="wrap_content"
android:layout_height="30.0dip"
android:layout_marginTop="30.0dip"
android:layout_marginRight="0.0dip"
android:text="#string/hello_world"
android:layout_alignParentRight="true"
style="?android:attr/buttonStyleSmall" />
</RelativeLayout>
as it might / might not be affected by the adapter view, i also put it here
public class SimpleAdapter extends ArrayAdapter implements Serializable {
private ArrayList<?> items;
private int itemLayout;
private String[] itemFieldsName;
private int[] itemElements;
private Context ctx;
public SimpleAdapter(Context ctx, ArrayList<?> items, int itemLayout, int[] itemElements, String[] itemFieldsName) {
super(ctx, itemLayout, items);
this.items = items;
this.itemElements = itemElements;
this.itemLayout = itemLayout;
this.itemFieldsName = itemFieldsName;
this.ctx = ctx;
}
public View getView(int position, View convertView, ViewGroup parent) {
View itemView = convertView;
if (itemView == null) {
itemView = ((LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(this.itemLayout, null);
}
Object item = this.items.get(position);
for (int i = 0; i < this.itemElements.length; i++) {
Object value = getValue(item, Character.toUpperCase(this.itemFieldsName[i].charAt(0)) + this.itemFieldsName[i].substring(1));
View elementView = null;
if (itemView != null) {
elementView = itemView.findViewById(this.itemElements[i]);
if ((elementView instanceof TextView)) {
((TextView) elementView).setText(value.toString());
} else if ((elementView instanceof SmartImageView)) {
((SmartImageView) elementView).setImageUrl(value.toString());
} else if ((elementView instanceof Button)) {
((Button) elementView).setText(value.toString());
}
}
}
return itemView;
}
private Object getValue(Object obj, String fieldName) {
ArrayList<String> fieldsName = new ArrayList<String>();
int length;
String str = Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
Object value = new Object();
try {
value = obj.getClass().getMethod("get" + str, new Class[0]).invoke(obj);
} catch (Exception ex) {
value = fieldName;
}
return value;
}
private String trimString(String string, int length, boolean soft) {
if(string == null || string.trim().isEmpty()){
return string;
}
StringBuffer sb = new StringBuffer(string);
int actualLength = length - 3;
if(sb.length() > actualLength){
if(!soft)
return sb.insert(actualLength, "...").substring(0, actualLength+3);
else {
int endIndex = sb.indexOf(" ",actualLength);
return sb.insert(endIndex,"...").substring(0, endIndex+3);
}
}
return string;
}
}
Can someone tell me where i was wrong?,
tell me if you need more explanation / code resource
Finding an inner view inside an inflated layout is among the most common operations in Android. This is usually done through a View method called findViewById(). This method will recursively go through the view tree looking for a child with a given IDcode. Using findViewById() on static UI layouts is totally fine but, as you’ve seen, ListView calls the adapter’s getView() very frequently when scrolling. findViewById() might perceivably hit scrolling performance in ListViews—especially if your row layout is non-trivial.
The View Holder pattern is about reducing the number of findViewById() calls in the adapter’s getView(). In practice, the View Holder is a lightweight inner class that holds direct references to all inner views from a row. You store it as a tag in the row’s view after inflating it. This way you’ll only have to use findViewById() when you first create the layout. Here’s the code sample with View Holder pattern applied:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.your_layout, null);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
convertView.setTag(holder);
} else {
holder = convertView.getTag();
}
holder.text.setText("Position " + position);
return convertView;
}
private static class ViewHolder {
public TextView text;
}
There is one known issue with android:singleLine where it kills performance on ListViews drastically. Please see this question Why does android:singleLine="true" make ListView scrolling very laggy?
Remove it from your layout and use maxLines instead.
i have a problem in my app:
i use a listview with a personalized adapter,
in this adapter i want to change the color of the line depending on whether the message is read or not.
In the metod GETVIEW i control a variable, if it is equal to 0 i want to change the background color.
All works and the list is displayed as i want,
but when there are a lot of elements and the list is scrolled in any direction (from top to bottom and vice versa) the raws are displeyed with the same color even if by code is set another color.
Has anyone ever had the same problem?
You can advise me something about it?
There is the code of my Adapter:
public class LazyAdapterComunicazioni extends BaseAdapter {
private Activity activity;
private String[] id;
private String[] titolo;
private String[] data;
private String[] letto;
private static LayoutInflater inflater=null;
//public ImageLoader imageLoader;
public LazyAdapterComunicazioni(Activity a, String[] idCom, String[] titoloCom, String[] dataCom, String[]lettoCom) {
activity = a;
id = idCom;
titolo = titoloCom;
data = dataCom;
letto = lettoCom;
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return id.length;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View vi = convertView;
if(convertView == null)
{
vi = inflater.inflate(R.layout.comunicazionicslist, null);
}
ContactsViewHolder viewHolder = new ContactsViewHolder();
//Settimane
viewHolder.txtTitolo=(TextView)vi.findViewById(R.id.comCS_Titolo);
viewHolder.txtTitolo.setText(titolo[position].toString());
//Data
viewHolder.txtData=(TextView)vi.findViewById(R.id.comCS_Data);
viewHolder.txtData.setText(data[position].toString());
//ID
viewHolder.txtID=(TextView)vi.findViewById(R.id.comCS_ID);
viewHolder.txtID.setText(id[position].toString());
//Connessianne e Apretura del DB
String read = letto[position].toString();
if (read.equals("0")) //DA LEGGERE
{
//LAYOUT
viewHolder.rel = (RelativeLayout)vi.findViewById(R.id.comCS_RIGA);
viewHolder.rel.setBackgroundResource(R.drawable.sfondorigacomcs);
viewHolder.txtTitolo.setTextColor(Color.WHITE);
}
return vi;
}
static class ContactsViewHolder {
TextView txtTitolo;
TextView txtData;
TextView txtID;
RelativeLayout rel;
}
}
and the xml of the single row:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="#+id/comCS_RIGA"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#ffffff"
android:paddingTop="10dp"
android:paddingBottom="10dp"
>
<TextView
android:id="#+id/comCS_Data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textColor="#000000"
android:layout_alignParentLeft="true"
android:layout_marginLeft="16dp"
android:textSize="12sp"
/>
<TextView
android:id="#+id/comCS_ID"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="#+id/comCS_Data"
android:visibility="invisible"
/>
<TextView
android:id="#+id/comCS_Titolo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/comCS_Data"
android:layout_marginTop="2dp"
android:layout_marginLeft="16dp"
android:text=""
android:textColor="#357cbc"
android:textSize="18sp"
/>
<ImageView
android:id="#+id/feedbackCars_Positivo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/frecciacom"
android:contentDescription="#string/title_Comunicazioni"
android:layout_alignParentRight="true"
android:layout_marginTop="2dp"
android:layout_marginRight="16dp"
/>
</RelativeLayout>
It's a common error.
Views are recycled so you have to set back default values in any cases.
if (read.equals("0")) //DA LEGGERE
{
//LAYOUT
viewHolder.rel = (RelativeLayout)vi.findViewById(R.id.comCS_RIGA);
viewHolder.rel.setBackgroundResource(R.drawable.sfondorigacomcs);
viewHolder.txtTitolo.setTextColor(Color.WHITE);
}
else {
viewHolder.rel.setBackgroundResource("you_defaulf_bg_res");
}
First of all, you are not using the ViewHolder pattern correctly. The ViewHolder is designed to hold references to the Views of the list-item and minimize the findViewById(...) calls.
That purpose is not fulfilled by your code. The reason for the behaviour you described lies in the Views not being recycled.
Do it like this:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View vi = convertView;
ContactsViewHolder viewHolder;
if(convertView == null) {
vi = inflater.inflate(R.layout.comunicazionicslist, null);
viewHolder = new ContactsViewHolder();
viewHolder.txtTitolo=(TextView)vi.findViewById(R.id.comCS_Titolo);
viewHolder.txtData=(TextView)vi.findViewById(R.id.comCS_Data);
viewHolder.txtID=(TextView)vi.findViewById(R.id.comCS_ID);
viewHolder.rel = (RelativeLayout)vi.findViewById(R.id.comCS_RIGA);
vi.setTag(viewHolder);
} else {
viewHolder = (ContactsViewHolder) vi.getTag();
}
//Settimane
viewHolder.txtTitolo.setText(titolo[position].toString());
//Data
viewHolder.txtData.setText(data[position].toString());
//ID
viewHolder.txtID.setText(id[position].toString());
//Connessianne e Apretura del DB
String read = letto[position].toString();
if (read.equals("0")) //DA LEGGERE {
viewHolder.rel.setBackgroundResource(R.drawable.sfondorigacomcs);
viewHolder.txtTitolo.setTextColor(Color.WHITE);
} else {
viewHolder.rel.setBackgroundResource(R.drawable.NORMAL_BACKGROUND);
}
return vi;
}