I have a listview (which has a textview in its items) and a button in my activity. And I'm loading the data into listview using loaders mechanism. And I'm starting settings activity of app on click of the button. User can change font type and size in settings screen. And I'm getting changed font values from preferences and setting it to textviews inside bindView() method of my custom adapter class. But I'm able to see the changed font in listview only when I start the activity again. How can I apply changed font size and type to listview items as soon as user clicks back button from settings screen? How can I refresh my listview for only to change the font? Please guide me.
I have tried the below to refresh the listview but it was of no use as it notifies only when the adapter dataset is changed.
#Override
public void onResume() {
super.onResume();
adapter.notifyDataSetChanged();
}
Call this method after changing font. In this paramter "v" is your listview or listview parentand "f" is your font in method.
/**
* To apply custom font for whole activity
*/
public static void applyFontForWholeActivity(final View v) {
// Typeface fontToSet = Typeface.createFromAsset(v.getContext()
// .getAssets(), "fonts/EUROSTILE_BOLD.TTF");
try {
if (v instanceof ViewGroup) {
ViewGroup vg = (ViewGroup) v;
for (int i = 0; i < vg.getChildCount(); i++) {
View child = vg.getChildAt(i);
applyFontForWholeActivity(child);
}
} else if (v instanceof TextView) {
TextView tv = (TextView) v;
tv.setTypeface(ActivityUtils.fontToSetGlobal);
}
} catch (Exception e) {
e.printStackTrace();
// ignore
}
}
Set your adapater again:
#Override
public void onResume() {
super.onResume();
listview.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
Related
I'm working on an app in which i need to show some data in listview for that i've created an activity which is connected to the layout containing listview , for that listview i've created custom listitem. Now i need to access the components( Imageview) from custom list item in activity. Can anyone please tell me how to achieve this ?
This is the activity
package com.example.manishnegi.sharemyride;
public class RideMatched extends Activity {
int commentCount = 0;
private List<GetRidesSummaryDetails> oslist = new ArrayList<GetRidesSummaryDetails>();
ListView rides_matchedListview;
ImageView hrate1,hrate2,hrate3,hrate4,hrate5;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ride_matched);
hrate1=(ImageView)findViewById(R.id.hrate1);
hrate2=(ImageView)findViewById(R.id.hrate2);
hrate3=(ImageView)findViewById(R.id.hrate3);
hrate4=(ImageView)findViewById(R.id.hrate4);
hrate5=(ImageView)findViewById(R.id.hrate5);
int l=0;
String arr;
JSONArray array=new JSONArray();
Bundle b=getIntent().getExtras();
if(b!=null)
{
l=b.getInt("array_length");
arr=b.getString("rides_array");
try {
array=new JSONArray(arr);
Log.e("String to array ",array.toString());
} catch (JSONException e) {
e.printStackTrace();
Log.e("String to array",e.getMessage());
}
Log.e("Number of matched",l+"");
}
rides_matchedListview = (ListView)findViewById(R.id.rides_matchedListview);
Second scnd = new Second();
List<GetRidesSummaryDetails> detailsofrides = scnd.getallcomments(l,array);
for (GetRidesSummaryDetails values :detailsofrides){
String driver_imageget = values.getDriverimage();
String driver_nameget = values.getDrivername();
String pickup_get = values.getPickuptime();
String ride_idget= values.getRideId();
String rating_get = values.getRating();
String vehicle_imageget = values.getVehicleImage();
GetRidesSummaryDetails vehi = new GetRidesSummaryDetails();
vehi.setDriverimage(driver_imageget);
vehi.setDrivername(driver_nameget);
vehi.setPickuptime(pickup_get);
vehi.setRideId(ride_idget);
vehi.setRating(rating_get);
setRating(Integer.parseInt(rating_get));
vehi.setVehicleImage(vehicle_imageget);
oslist.add(vehi);
commentCount++;
rides_matchedListview.setAdapter(new RidesMatchedAdapter(RideMatched.this, 0, oslist));
new RidesMatchedAdapter(RideMatched.this ,0, oslist).notifyDataSetChanged();
}
}
public void setRating(int rate)
{
if (rate == 1) {
hrate1.setImageResource(R.drawable.star);
}
if (rate == 2) {
hrate1.setImageResource(R.drawable.star);
hrate2.setImageResource(R.drawable.star);
}
if (rate == 3) {
hrate1.setImageResource(R.drawable.star);
hrate2.setImageResource(R.drawable.star);
hrate3.setImageResource(R.drawable.star);
}
if (rate == 4) {
hrate1.setImageResource(R.drawable.star);
hrate2.setImageResource(R.drawable.star);
hrate3.setImageResource(R.drawable.star);
hrate4.setImageResource(R.drawable.star);
}
if (rate == 5) {
hrate1.setImageResource(R.drawable.star);
hrate2.setImageResource(R.drawable.star);
hrate3.setImageResource(R.drawable.star);
hrate4.setImageResource(R.drawable.star);
hrate5.setImageResource(R.drawable.star);
}
}
}
This is the listview layout(ride_matched.xml)
This is the custom list item
You must've created the adapter for the listview using that adapter declare the images inside viewHolder it should work.
Use any adapter (according to your need) to inflate data in the ListView. In the adapter itself, you can access the components of custom listitem.
You cannot access the components of custom listitem inside the activity. They can only be accessed inside the adapter inflating the data in the ListView.
We need to pass adapter to the listview. Adapters contain list of model object to be shown in list . You can create a custom adapter extending the base adapter. You can follow the below tutorial
http://androidexample.com/How_To_Create_A_Custom_Listview_-_Android_Example/index.php?view=article_discription&aid=67&aaid=92
p[ay attention to getView method in the adapter and change the layout name as per you custom layout in the following line of get view
vi = inflater.inflate(R.layout.tabitem, null);
Create a holder class where it has all the view item required to display in your list.
You need not to access Image view from activity as it can be accessed in the getView method for each row iteration.
I have a ListView in an Android Activity and a custom adapter for that listview.
I want to be able to edit a row item and update that row instantly. This works, the modifications of the row is seen But, on scroll i loose all data.
This is my Asynk task from where i get the data and update the list row item:
/**
*
*/
public class EditNewsFeedPostAsyncTask extends AsyncTask<Void, Void, Boolean> {
public Activity context;
public String content;
public int rowPosition;
public ListView listView;
public TextView decriptionTxt;
#Override
protected Boolean doInBackground(Void... params) {
try {
token = Utils.getToken(context);
if (token != null) {
....
// {"status":"true"}
if (result != null) {
....
}
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
#Override
protected void onPostExecute(final Boolean success) {
if (success) {
updateListView(rowPosition, content);
}
}
public boolean updateListView(int position, String content) {
int first = listView.getFirstVisiblePosition();
int last = listView.getLastVisiblePosition();
if (position < first || position > last) {
return false;
} else {
View convertView = listView.getChildAt(position - first);
decriptionTxt.setText(content);
listView.invalidateViews();
return true;
}
}
private void updateView(int index, TextView decriptionTxt) {
View v = listView.getChildAt(index - listView.getFirstVisiblePosition());
if (v == null)
return;
decriptionTxt.setText(content);
listView.invalidateViews();
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected void onCancelled() {
}
}
What am i missing? shouldn't the data be persistent?
Thx
You must update the object in your listView adapter, not only the views!
after scrolling, the getView method inside your list's adapter will call and you will return the default view for that.
if you want to change that item permanent, you should update your data set and call notifyDataSetChanged on your adapter.
Make sure you're updating the data, not just the view.
When you modify the row, are you changing the underlying data or just the view? If just the view...
You're probably running into ListView recycling issues. This answer has a great explanation. Basically, ListViews are about efficiency in displaying views based on data, but are not good for holding new data on screen. Every time a ListView item is scrolled out of view, its View is recycled to be used for the item that just scrolled into view. Therefore, if you put "hi" in an EditText and then scroll it off screen, you can say goodbye to that string.
I solved this in my app by ditching ListView altogether and using an array of LinearLayouts (probably a clunky approach, but I had a known list size and it works great now). If you want to continue using a ListView, you'll have to approach it from a "data first" perspective. Like I said, ListViews are great at showing info from underlying data. If you put "hi" in an EditText and simultaneously put that string in the underlying data, it would be there regardless of any scrolling you do. Updating onTextChanged might be cumbersome, so you could also let each row open a dialog in which the user enters their data which then updates the underlying dataset when the dialog closes.
These are just some ideas, based on some assumptions, but editing views in a ListView is, in general, not very in line with how ListViews work.
I have an activity that has a single listview in it with enough items to extend of the page.
I want to set a certain listView item at position i to a different drawable.
To go this I use the line of code..
listView.getChildAt(selector).setBackgroundResource(R.drawable.main_button_shape_pressed);
There is a very confusing problem going in. This line of code is setting two listView items to the specified drawable.
When i = 0 item 0 and item 11 are set to that drawable. It turns out that when I call this line of code with i both item i and item i+11 are set to that drawable. This is rather baffling. Then to mix EVERYTHING when I start the activity in landscape, it is a different second listview that gets set to that drawable. And in certain scenarios when I change from portrait to landscape, the current highlight listview item on screen will change to a different one.
WTF is going on with the listview class? Are the indexes to it children constantly pointing to different things?
Here is my entire activity.
public class SelectorActivity extends Activity {
private ListView listView;
private int selector;
private boolean set;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.selector_layout);
set=false;
Bundle extras = getIntent().getExtras();
if(extras!=null)
{
selector=extras.getInt("selector");
}
listView=(ListView)findViewById(R.id.selector_layout);
//set the string array for the listview
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
R.array.sounds_array, android.R.layout.simple_list_item_1);
adapter.setDropDownViewResource(android.R.layout.simple_list_item_1);
listView.setBackgroundResource(R.drawable.listview_background);
listView.setAdapter(adapter);
highlightSelected();
}
//this method will highlight a selected listview once that listview is drawn
private void highlightSelected()
{
if(!set)
{
new Thread(
new Runnable()
{
#Override
public void run() {
// TODO Auto-generated method stub
boolean trigger=true;
while(trigger)
{
if(listView.getChildAt(selector)!=null)
{
set=true;
trigger=false;
listView.getChildAt(selector).setBackgroundResource(R.drawable.main_button_shape_pressed);
}
}
}
}
).start();
}
}
}
ListViews recycle their children. While drawing itself, the ListView will create a new view for every visible child. When you scroll, it will then re-use the last view that became non-visible (scrolled off the screen) as the next view in the list. That is why it's a different view index in landscape and that is why it would probably be a different view index on a device with a different screen size.
The solution should be to reset the view background in the Adapter's getView() method.
Additionally, touching views on anything other than the UI (main) thread is a bad practice. Check the selected item index in the getView() method and set the background right there. You'll also need to handle the case where the selected index changes (unless it never changes after this activity is created) by iterating over visible views in the listview and setting their backgrounds to the appropriate values.
// Must be final to use inside the ArrayAdapter
final int selector = extras == null ? -1 : extras.getInt("selector");
ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(
this,
R.array.sounds_array,
android.R.layout.simple_list_item_1) {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View newView = super.getView(position, convertView, parent);
// set the background according to whether this is the selected item
if (position == selector) {
// this is the selected item
newView.setBackgroundResource(R.drawable.main_button_shape_pressed);
} else {
// default background for simple_list_item_1 is nothing
newView.setBackground(null);
}
return newView;
}
};
I have an activity which has a TextView button (btnChangeMode) that toggles the mode from "admin" to "guest". Depending on the mode chosen, I need to hide/show a button (btnAddListItems) within my listview row. The code i have, currently doesn't seem to be cutting it.
Code speaks easier, so here's the gist of my code:
My activity layout:
<FrameLayout>
<ListView> ... </ListView> <!-- which has its items populated from myCustomAdapter -->
<TextView> ... </TextView> <!-- this is my btnChangeMode -->
<FrameLayout>
nothing fancy in my activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity);
List listItems = ... // some method that gets objects from Database
ListView listView = (ListView) findViewById(R.id.list_view_id);
listView.setAdapter(new MyCustomAdapter(this, listItems));
}
I have a custom adapter that basically has two types of rows header & item. In the "header" row, I have button "btnAddListItems" that allows me to add items into the list view. I want this button to be visible only in admin mode.
I've overridden the necessary methods in myCustomAdapter (getviewTypeCount, getItemViewType, getCount and getView). here's the getView method:
#Override
public View getView(int position, View row, ViewGroup parent) {
if (row == null) {
if (getItemViewType(position) == ITEM_VIEW_TYPE_HEADER) {
return getHeaderRow();
} else {
return getItemRow();
}
}
if (getItemViewType(position) == ITEM_VIEW_TYPE_ROW) {
MyHolder holder = (MyHolder) row.getTag();
holder.populateNewContent();
}
return row;
}
....
private View getHeaderRow() {
View lRow = LayoutInflater.from(getContext()).inflate(R.layout.my_header, null);
mViewMode = new DetailsViewMode((Activity) getContext(), getChangeViewModeListener(lRow));
return lRow;
}
So mViewMode here is a convenience POJO class i wrote that contains btnChangeMode, a boolean variable that indicates current mode (isAdmin) and the caller activity. I don't believe there's anything specific to my problem, so i'm not including that code here. Will be glad to if someone thinks that'll help.
private ChangeViewModeFragment.ChangeViewModeListener getChangeViewModeListener(final View headerRow) {
return new ChangeViewModeFragment.ChangeViewModeListener() {
#Override
public void onViewModeChanged(boolean isViewModeAdmin) {
mViewMode.changeViewModeButtonText(isViewModeAdmin);
toggleAdminFeatures(isViewModeAdmin, headerRow);
}
};
}
private void toggleAdminFeatures(boolean isViewModeAdmin, View headerRow) {
TextView btnAddListItems = (TextView) headerRow.findViewById(R.id.add_button_id);
if (isViewModeAdmin) {
btnAddListItems.setVisibility(View.VISIBLE);
} else {
btnAddListItems.setVisibility(View.GONE);
}
}
This is the part that's not working as it should. btnAddListItems is always visible within my listview.
btnChangeMode is within my activity so to speak, while the btnAddListItems is within my Adapter (ListView). But my requirement necessitates this behavior of having the listner of an activity, change the row state of my listview.
I suspect that when i change the visibility of my header row's button, I don't have hold of the correct header row instance, if that makes sense :P.
NitroNbg's suggestion of having a private button didn't work, which leads me to believe that maybe the ListView just needs a kickstart to get refreshed?
But I've tried calling notifyDataSetChanged() at the end of my toggleAdminFeatures method but that doesn't seem to be doing the trick.
I'd try the following - create a private Button within your adapter class and within your getView() method, put a reference to btnAddListItems to it.
private Button buttonToHide;
//...
public View getView(...) {
//...
buttonToHide = (Button) row.findViewById(R.id.add_button_id);
//...
}
Then, inside your ChangeViewModeListener() simply refer to a method of your adapter class (of course you'll have to write it) that sets the buttonToHide.setVisibility(View.INVISIBLE)
Hopefully, since it's within the adapter it would be accessible.
EDIT: Just to point out if it isn't obvious - only refer to the button that's in the header row.
You don't need to notifyDataSetChanged(), coz dataset hasn't changed, try setting the visiblity to INVISIBLE
I'm creating an Android app with an activity comprised of a mixture of static views and dynamically created views. The activity contains UI elements held in an XML file and UI elements which are created dynamically based on the content of a model class. (as in, there is an array in my model class and an EditText will be created for every element in the array.. and a Spinner will be created containing every element in a different array)
What's the usual method for saving and restoring the state of the application when dynamically created UI elements are involved? Because I won't always know which UI elements will exist at any one time! Currently, my binding code simply reloads the data from the database when the device orientation changes, losing any changes that the user made.
I've had a good look around on Google/SO for this and I've not found anything related to this problem.
Thanks all
Edit:
For anyone that comes across this in the future, I did a slightly modified version of Yakiv's approach and wound up with this:
for (int i = 0; i < views.size(); i++) {
View currentView = views.get(i);
if (currentView instanceof CheckBox) {
outState.putBoolean("VIEW"+i, (((CheckBox) currentView).isChecked()));
} else if (currentView instanceof EditText) {
outState.putString("VIEW"+i, ((EditText) currentView).getText().toString());
} else if (currentView instanceof Spinner) {
//.....etc. etc.
}
}
Thanks again Yakiv for the awesome idea.
To restore dynamic views state you need to make them as class fields and initialize them in onCreate. Then just use this article to save and restore their state.
private List<View> mViews= new ArrayList<View>();
#Override
public void onCreate(..)
{
LinearLayout parent = ....//initialize it here
initializeViews();
}
public void initializeViews()
{
// create and add 10 (or what ever you need) views to the list here
mViews.add(new View(this));
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
int mViewsCount = 0;
for(View view : mViews)
{
savedInstanceState.putInt("mViewId_" + mViewsCount, view.getId());
mViewsCount++;
}
savedInstanceState.putInt("mViewsCount", mViewsCount);
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
int mViewsCount = savedInstanceState.getInt("mViewsCount");;
for(i = 0; i <= mViewsCount)
{
View view = mViews.get(i);
int viewId = savedInstanceState.getInt("mViewId_" + i);
view.setId(viewId);
mViewsCount++;
}
}
Best wishes.