While this question may sound like a dupe of some others, the fact I am trying to do this in a widget significantly limits my ability to use the proposed solutions elsewhere.
Summarized: I would like to add spacing above the top-most item in the list and below the bottom-most item in order to offset them away from the title bar and bottom bar edge.
Essentially, I am trying to solve the same problem as Add margin above top ListView item (and below last) in Android, however, I cannot rely on having a reference to my ListView object in order to setHeader() or anything like that.
Is this really impossible to do in an Android widget?
I assume you are using an adapter for your ListView, so you are inflating additional listview layout. In a getView method, according to list item position (0 and last), place your bottom or top padding.
So ,here i have override function of adapter.From this function you can change or specify margin,height.Here you need to take object of Layout which you are using and from this you can change layout attributes.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater mInflater = (LayoutInflater) context
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.drawer_list_item, null);
}
if (position == 0) {//for first row
ImageView imgIcon = (ImageView) convertView.findViewById(R.id.icon);
TextView txtTitle = (TextView) convertView.findViewById(R.id.title);
RelativeLayout.LayoutParams relate = new RelativeLayout.LayoutParams(200, 200);
relate.setMargins(150, 0, 0, 0);
imgIcon.setLayoutParams(relate);
RelativeLayout.LayoutParams text = new RelativeLayout.LayoutParams(300, 100);
text.setMargins(150, 180, 0, 0);
}
return convertView ;
//HOPE IT HELP
in the adapter during getView() check if it's either position 0 or .getLength() and set LayoutParams on the convertView there
Related
I am using ListView to show TextViews in some rows and not others. I don't know how many items there will be, it runs fine but when I scroll down the display changed.
For example:
If I have 10 items in ListView, and only 5 of them are currently visible. Adapter returns positions from 0 to 4 correctly...thats ok. When I scroll down position of item 10 and return to the top, I get all my items looking like the first item on the list, and if I scroll many times I get the first item repeated 10 times.
Im using ArrayAdapter.
Here's some code:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
PlanningHolder holder = null;
if (row == null) {
row = mInflater.inflate(layoutResourceId, parent, false);
holder = new PlanningHolder();
holder.numberOfJour = (TextView) row.findViewById(R.id.numberDate);
holder.lay = (LinearLayout) row.findViewById(R.id.layPlage);
row.setTag(holder);
} else {
holder = (PlanningHolder) row.getTag();
}
if(position%2 == 1){
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
params.setMargins(50, 5, 30, 5);
//MarginLayoutParams marginLayoutParams = (MarginLayoutParams) params.getLayoutParams();
//marginLayoutParams.setMargins(marginleft, 5, display.getWidth() - marginright, 5);
TextView newplage = new TextView(context);
newplage.setLayoutParams(params);
newplage.setBackgroundColor(context.getResources().getColor(R.color.darkorrange));
holder.lay.addView(newplage);
}
holder.numberOfJour.setText(myPlannig.getCurrent().get(Calendar.DAY_OF_MONTH) + "");
row.setId(position);
return row;
}
and this is my listView declaration:
<ListView
android:id="#+id/list_planning"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/top_shadow"
android:scrollingCache="true"
android:dividerHeight="1dp">
</ListView>
The problem is caused by reusing convertView. Either disable faulty optimization
View row = null;
or repair optimization by implementing else branch of if and set row views numberDate, layPlage appropriately for this case.
Notice that you are adding views to holder.lay but you never remove them. They accumulate when you are scrolling and reusing convertView.
This is a common case in list view. You must handle if and else case correctly.
For example, if you set text in if case and not in else case, then the old value gets retained. The views in list view are getting recycled.
When you first open the list view, it is getting created.
When you scroll back and forth, You are loading data over an already existing view which already had values.
Whatever you handle in the if case within the getView, you must probably undo it in the else. In other words, you must handle the views in every case or path the code flows in the getView method.
Put else condition like this
else
{
lay.addView(newplage);
}
I'm working on a ListView based app and I have a very weird problem, my ListItems are reappering and the correct item is not shown in the correct spot. For the sake of making this easy to understand I've set the text on each ListItem to be the same as it's position. I'm doing this in my adapters getView() call. If I have my Nexus 7 4 ListItems are visible. If I have a total of 10 ListItems then it will go like 0, 1, 3, 4, 0, 1, 2, 3, 4. This goes for all devices meaining that the number of items initially on screen + 1 will be correct while all other ListItems are rearrenged.
In which part of my code do you guys think my problem lies because right now I've been trying to fix this for hours and I'm clueless. All help is very much appreciated.
EDIT:
Here's my getView():
#Override
public View getView(int position, View convertView, ViewGroup parent) {
CountdownItem ci = mTitle.get(position);
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item, parent, false);
holder = new CountdownViewHolder();
holder.mTitle = (TextView) convertView.findViewById(R.id.textPrim);
holder.mSubtitle = (TextView) convertView
.findViewById(R.id.textSec);
holder.mDayProgress = (ProgressBar) convertView
.findViewById(R.id.day_progress);
holder.mMonthProgress = (ProgressBar) convertView
.findViewById(R.id.month_progress);
holder.mYearText = (TextView) convertView
.findViewById(R.id.year_text);
holder.day_help = (TextView) convertView
.findViewById(R.id.day_help);
holder.month_help = (TextView) convertView
.findViewById(R.id.month_help);
holder.setTitle(Integer.toString(position) + " Title");
holder.setSubtitle(ci.getSubtitle());
holder.fixImageAndText(position);
convertView.setTag(holder);
} else {
holder = (CountdownViewHolder) convertView.getTag();
}
return convertView;
}
You aren't using the ViewHolder pattern correctly. The following code needs to be moved outside the if/else clause and before return convertView:
holder.setTitle(Integer.toString(position) + " Title");
holder.setSubtitle(ci.getSubtitle());
holder.fixImageAndText(position);
This is the correct behaviour for the listview when it is reusing cells, the problem is that you only set the values when the cell is first created.
When convertView == null the listview has no cell to recycle. However, once it has created a few it can reuse them to display as you scroll.
What you need to do is set the title and subtitle even when convertView is not null. That way you're setting them for each new list position.
Yes, this is because android reuses views in lists, to increment performance and rendering speed.
The holder pattern is used to store views ids. After you retrieve them, you have to set the text you want to see inside.
For example, you retrieve your data (e.g. myDataArray[position]), and if it's all ok, you proceed setting title, subtitle, dayprogress, etc. with TextView's setText().
I have a list view in which I add items dynamically. I want that some items ( of my choice) should align to the left hand side and some to the right hand side. Is this thing possible with the ListView of Android??
//Adapter
if (convertView == null) {
holder = new EventViewHolder();
if (type == 1) {
convertView = inflater.inflate(
R.layout.multi_line_list_item, null);
holder.mtvMessage = (TextView) convertView
.findViewById(R.id.tvMessage);
holder.mtvMessage.setGravity(Gravity.LEFT);
holder.mtvMessage.setBackgroundResource(R.drawable.chatbluebox);
convertView.setTag(holder);
}else{
convertView = inflater.inflate(
R.layout.multi_line_list_item, null);
holder.mtvMessage = (TextView) convertView
.findViewById(R.id.tvMessage);
holder.mtvMessage.setGravity(Gravity.RIGHT);
holder.mtvMessage.setBackgroundResource(R.drawable.chatgreenbox);
convertView.setTag(holder);
}
Please let me know if it can be done and suggest me a good solution?
android:stackFromBottom="true"
android:transcriptMode="alwaysScroll"
Add these lines in your listView xml entry..,.
And for right and left alignment, maintain two layouts xmls one right align and other left align(Whatever look you want) and then in getView() method of listAdapter set some condition and inflate these xml as you want
if (convertView == null)
if(some condition)
convertView = mInflater.inflate(R.layout.rightxml, null);
else
convertView = mInflater.inflate(R.layout.leftxml, null);
....
Have a look at the ListView modes stackFromBottom and transcriptMode those may do most of the work for you.
To align the items in the Cells left and right you can, for example set the gravity to left and right in your Adapters getView Method.
Use following.
adapter.notifyDataSetChanged();
And then
listView.setSelection(items.size()-1);
For gravity You need to set gravity dynamically in your getview() method.
FOr example :-
textview1.setGravity(Gravity.RIGHT);
For my application i retrieve a number from the database. When the activity starts up it has to show the number in a different color then the other numbers in the list.
After retrieving the data from the database this is my code:
int row = 5;
TextView child = (TextView)ListView.GetChildAt(row);
child.SetTextColor(Color.Red);
This code was placed in the OnCreate function. I kept getting a null value back for the child textview. I then found out that the reason for the null value is that in the OnCreate function the listview still needs to be rendered. I then moved the code to the OnStart() function but this didn't work either.
Can anyone tell me how I should retrieve the child row from the listview during the creation/start of the activity?
even if you will be able to do it this way you will experience problems with this view getting recycled .. (you will see other views getting colored with red when you scroll up and down).
You need to override your adapter and set the view's color in the position you want
under getView() -
TextView myText = (TextView) view.findViewById(R.Id ....
if (pos==5)
myText.setTextColor(Color.Red);
else
myText.setTextColor(Color.Black); //original color..
EDIT:
you don't need to have a custom xml. if you find android's xml you can find its id. I believe its android.R.id.text1 . so your adapter should look something like
myAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1) {
#Override
public View getView(int position, View v, ViewGroup parent) {
if (v == null) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(android.R.layout.simple_list_item_1, null);
}
View view = super.getView(position, v, parent);
if (position==5)
view.setTextColor(Color.Red);
else
view.setTextColor(Color.Black); //original color..
}
myList.setAdapter(myAdapter);
not sure I got it all right but something like that..
hope it helps.
I have this ListView:
I'm using a custom adapter.
As you see, each row is made of a checkbox, a big TextView, and a little TextView.
All items have defined the little TextView, even the "Item 2" but it's a void string.
The problem comes when I tap the EditText placed in the header of the list:
The keyboard appears, and the rows are recycled, so the getView method of my adapter is called. In that method I have an if clause where I check if the length of the "optional" text (the little TextView) is greater than 0. In that case I make some room (the space that you can see in the screenshot) and I display it.
The problem is that "Item 2" has the "optional" text initialized but it's void (0-sized). I don't understand why the if clause is executed. But more strangely... the else is also executed! In the else I just show a void string in the little TextView.
Why is this happening? The app is really simple. This is my getView method:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.list_row, null);
}
ListItem list_item = items.get(position);
TextView item_title = (TextView) convertView.findViewById(R.id.item_title);
TextView item_optional = (TextView) convertView.findViewById(R.id.item_optional);
item_title.setText(list_item.getTitle());
// If the task has an optional text, make some room and display it
if (list_item.hasOptional()) {
// This portion of code will be executed when you tap the EditText and the keyboard appears, putting the item up in the row
LayoutParams layout_params = (LayoutParams) item_title.getLayoutParams();
layout_params.topMargin = 10;
layout_params.height = -2; // -2: wrap_content
item_title.setLayoutParams(layout_params);
item_optional.setText(list_item.getOptional());
item_optional.setVisibility(0);
} else {
// This portion of code will ALSO be executed when you tap the EditText... why? this should not happen!
item_optional.setText("");
}
return convertView;
}
The source code can be seen here (github).
When you modify a recycled view you have no idea what the state of the view is, with respect to how it might have been customized by previous calls to getView. The view you are recycling is not a fresh-out-the-box inflation of R.layout.list_row. Think of it as a "second hand" or "used" view.
So I can see under if (list_item.hasOptional().. you make some modification to the item_title.getLayoutParams(). As a view created here may later be recycled for a list item that will fail the check if (list_item.hasOptional() under the else code block you must reset the values you modify to the default specified in the layout.