I am getting a weird error while setting up my adapter for listview in Android. I am developing a chat application, when i send any message it appears in listview, but when i try to receive the message it takes me to Catch-block in FutureTask class
I am using adapter.notifyDataSetChanged(); to let my adapter know of any change i am creating in the adapter.
My code is as follows
private void addNewMessage(MessageItems m) {
if(MainActivity.mymessage)
{
messages.add(m);
}else if(MainActivity.mymessage == false)
{
messages.add(m);
}
adapter.notifyDataSetChanged();
getListView().setSelection(messages.size()-1);
}
**Code of Adapter**
public class AwesomeAdapter extends BaseAdapter{
private Context mContext;
private ArrayList<MessageItems> mMessages;
public AwesomeAdapter(Context context, ArrayList<MessageItems> messages) {
super();
this.mContext = context;
this.mMessages = messages;
}
#Override
public int getCount() {
return mMessages.size();
}
#Override
public Object getItem(int position) {
return mMessages.get(position);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
MessageItems message = (MessageItems) this.getItem(position);
ViewHolder holder;
if(convertView == null)
{
holder = new ViewHolder();
convertView = LayoutInflater.from(mContext).inflate(R.layout.sms_row, parent, false);
holder.message = (TextView) convertView.findViewById(R.id.message_text);
convertView.setTag(holder);
}
else
holder = (ViewHolder) convertView.getTag();
holder.message.setText(message.getMessage());
LayoutParams lp = (LayoutParams) holder.message.getLayoutParams();
//check if it is a status message then remove background, and change text color.
if(message.isStatusMessage())
{
holder.message.setBackgroundDrawable(null);
lp.gravity = Gravity.LEFT;
holder.message.getResources().getColor(R.color.textFieldColor);
//holder.message.setTextColor(R.color.textFieldColor);
}
else
{
//Check whether message is mine to show green background and align to right
if(message.isMine())
{
holder.message.setBackgroundResource(R.drawable.speech_bubble_green);
lp.gravity = Gravity.RIGHT;
}
//If not mine then it is from sender to show orange background and align to left
else
{
holder.message.setBackgroundResource(R.drawable.speech_bubble_orange);
lp.gravity = Gravity.LEFT;
}
holder.message.setLayoutParams(lp);
holder.message.getResources().getColor(R.color.textFieldColor);
//holder.message.setTextColor(R.color.textColor);
}
return convertView;
}
private static class ViewHolder
{
TextView message;
}
#Override
public long getItemId(int position) {
//Unimplemented, because we aren't using Sqlite.
return position;
}
In FutureTask class it takes me to the following try-catch block when i try to set adapter.notifyDataSetChanged();
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
I know the problem somewhere lies in setting up the adapter for listview, i need to update it as i get a new message, but i am unable to get it fixed. Your help would be appreciated
Related
My app has a ListView and each row contains a TextView running a timer and a video player (ExoMediaPlayer) in each row
I refresh each row by listAdapter.notifyDataSetChanged() to update the timer TextView every second. It works fine as shown in the below code.
Thread t = new Thread() {
#Override
public void run() {
try {
while (!isInterrupted()) {
Thread.sleep(1000);
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
updateTextView();
}
});
}
} catch (InterruptedException e) {
}
}
};
t.start();
private void updateTextView() {
if (listView != null) {
for (int i = 0; i <= listView.getListChildCount(); i++) {
View v = listView.getListChildAt(i);
if (v != null) {
for(int x=firstVisibleRow;i<=lastVisibleRow;i++)
{
HomeListItem data;
data = listMockData.get(i);
HListAdapter.notifyDataSetChanged();
TextView t = (TextView) v.findViewById(R.id.tvTimer);
t.setText(data.getElapsedTime());
}
}
}
}
}
Adapter
public HListAdapter(Context context, ArrayList<HomeListItem> listData) {
c = context;
layoutInflater = LayoutInflater.from(context);
this.listData = listData;
}
#Override
public int getCount() {
return listData.size();
}
#Override
public Object getItem(int position) {
return listData.get(position);
}
#Override
public long getItemId(int position) {
return listData.indexOf(getItem(position));
}
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
newsItem = listData.get(position);
if (convertView == null) {
holder = new ViewHolder();
convertView = layoutInflater.inflate(R.layout.location_list_row, null);
holder.timer= (TextView) convertView.findViewById(R.id.timer);
holder.videoPlayer = (EMVideoView) convertView.findViewById(R.id.videoPlayer);
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
newsItem = listData.get(position);
holder.timer.setText(newsItem.getElapsedTime());
String videolink = "http://www.someurl.com/";
holder.videoPlayer.setVideoURI(Uri.parse(videolink ));
holder.videoPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
holder.videoPlayer.start();
}
});
return convertView;
}
static class ViewHolder {
TextView timer;
EMVideoView videoPlayer;
}
Problem
The above code works good and the timer TextView updates the time every second.
But the problem i am facing is the VideoPlayer is being reloaded every second, the video does not play due to HListAdapter.notifyDataSetChanged();
if i remove the notifyDataSetChanged() video plays on the listview but the timer TextView does not get updated.
Any possibility to refresh/update the TextView timer only every second.
Any logic or method to solve the problem and make the video play and also the timer to be updated every second?
I think your logic in updateTextView method is wrong. See my comments
private void updateTextView() {
if (listView != null) {
for (int i = 0; i <= listView.getListChildCount(); i++) {
View v = listView.getListChildAt(i);
if (v != null) {
for(int x=firstVisibleRow;i<=lastVisibleRow;i++) // what is x here ?!!
{
HomeListItem data;
data = listMockData.get(i);
HListAdapter.notifyDataSetChanged();
TextView t = (TextView) v.findViewById(R.id.tvTimer);
t.setText(data.getElapsedTime()); // here you are setting the same text over and over
}
}
}
}
}
Instead go with this
private void updateTextView() {
if (listView != null) {
for (int i = 0; i < listView.getListChildCount(); i++) {
View v = listView.getListChildAt(i);
HomeListItem data;
data = listMockData.get(firstVisibleRow + i);
TextView t = (TextView) v.findViewById(R.id.tvTimer);
t.setText(data.getElapsedTime());
}
}
}
no need of notifyDataSetChanged()
I have a BaseAdapter with 3 kind of layout which is used to put JSONObject to my ListView. The adapter getCount() returns correct number of items that should be displayed on the ListView, but it only display the first one.
I tried to find another response to this problem here, but i've found none.
This is my code:
public class PerfilInfoAdapter extends BaseAdapter {
public static final int VIEW_TYPE_TITULO = 0;
public static final int VIEW_TYPE_DESCRICAO = 1;
public static final int VIEW_TYPE_KEY_VALUE = 2;
private JSONArray list;
private Activity activity;
private ViewHolder viewHolder;
public PerfilInfoAdapter(Activity activity, JSONArray list) {
this.activity = activity;
this.list = list;
}
protected class ViewHolder {
TextView textViewTitulo;
TextView textViewDescricao;
TextView textViewKey;
TextView textViewValue;
}
#Override
public int getCount() {
Log.d("PerfilInfoAdapter", "Number of items in array: " + Integer.toString(this.list.length()));
return this.list.length();
}
#Override
public JSONObject getItem(int position) {
JSONObject json = null;
try {
json = this.list.getJSONObject(position);
} catch (JSONException e) {
e.printStackTrace();
}
return json;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
int retorno = -1;
JSONObject json = null;
try {
json = this.list.getJSONObject(position);
if (json.getString("key").equals("Titulo")) {
retorno = VIEW_TYPE_TITULO;
} else if (json.getString("key").equals("Descrição")
|| json.getString("key").equals("Sou")) {
retorno = VIEW_TYPE_DESCRICAO;
} else {
retorno = VIEW_TYPE_KEY_VALUE;
}
} catch (JSONException e) {
e.printStackTrace();
}
return retorno;
}
#Override
public int getViewTypeCount() {
return 3;
}
#Override
public View getView(int position, View container, ViewGroup viewGroup) {
System.out.println("getView " + position + " " + container);
this.viewHolder = null;
int type = this.getItemViewType(position);
if (container == null) {
this.viewHolder = new ViewHolder();
switch (type) {
case VIEW_TYPE_TITULO:
container = this.activity.getLayoutInflater().inflate(
R.layout.perfil_info_full_titulo, viewGroup, false);
this.viewHolder.textViewTitulo = (TextView) container
.findViewById(R.id.perfil_info_full_textViewTitulo);
break;
case VIEW_TYPE_DESCRICAO:
container = this.activity.getLayoutInflater().inflate(
R.layout.perfil_info_full_descricao, viewGroup, false);
this.viewHolder.textViewDescricao = (TextView) container
.findViewById(R.id.perfil_info_full_textVewDescricao);
break;
case VIEW_TYPE_KEY_VALUE:
container = this.activity.getLayoutInflater().inflate(
R.layout.perfil_info_list, viewGroup, false);
this.viewHolder.textViewKey = (TextView) container
.findViewById(R.id.perfil_info_full_chave_valor_textFieldChave);
this.viewHolder.textViewValue = (TextView) container
.findViewById(R.id.perfil_info_full_chave_valor_textFieldValor);
break;
}
container.setTag(this.viewHolder);
} else {
this.viewHolder = (ViewHolder)container.getTag();
}
try {
JSONObject json = this.list.getJSONObject(position);
switch (type) {
case VIEW_TYPE_TITULO:
this.viewHolder.textViewTitulo.setText(json.getString("value"));
break;
case VIEW_TYPE_DESCRICAO:
this.viewHolder.textViewDescricao.setText(json
.getString("value"));
break;
case VIEW_TYPE_KEY_VALUE:
this.viewHolder.textViewKey.setText(json.getString("key"));
this.viewHolder.textViewValue.setText(json.getString("value"));
break;
}
} catch (JSONException e) {
e.printStackTrace();
}
return container;
}
}
This is what my log returns:
10-26 09:42:30.568: D/PerfilInfoAdapter(17228): Number of items in
array: 11
Another important information is that my ListView is inside another GridView, which has 4 different kinds of views, the gridView is working perfectly, but not the ListView.
public class PerfilAdapter extends BaseAdapter {
private List<JSONObject> jsonList;
private Activity activity;
private PerfilHelper helper;
private ImageLoader imageLoader;
private ViewHolder viewHolder;
private boolean exibirFull;
public static final int VIEW_TYPE_FOTO_PRINCIPAL = 0;
public static final int VIEW_TYPE_INFO = 1;
public static final int VIEW_TYPE_INFO_LIST = 2;
public static final int VIEW_TYPE_GALERIA = 3;
public PerfilAdapter(Activity activity, List<JSONObject> json, PerfilHelper helper) {
this.activity = activity;
this.helper = helper;
this.jsonList = json;
this.exibirFull = true;
if (!ImageLoader.getInstance().isInited()) {
ImageLoader.getInstance().init(new ImageLoaderConfiguration.Builder(this.activity).build());
}
imageLoader = ImageLoader.getInstance();
}
public void exibirFull(boolean exibir) {
this.exibirFull = exibir;
this.notifyDataSetChanged();
}
#Override
public int getCount() {
return this.jsonList.size();
}
#Override
public Object getItem(int i) {
return this.jsonList.get(i);
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public int getItemViewType(int position) {
int retorno = -1;
if (this.jsonList.get(0).has("foto")) {
if (position == 0) {
retorno = VIEW_TYPE_FOTO_PRINCIPAL;
}
else if (position == 1) {
retorno = VIEW_TYPE_INFO;
}
else if (position == 2) {
if (this.exibirFull) {
retorno = VIEW_TYPE_INFO_LIST;
}
else {
retorno = VIEW_TYPE_GALERIA;
}
}
else {
retorno = VIEW_TYPE_GALERIA;
}
}
else {
if (position == 0) {
retorno = VIEW_TYPE_INFO;
}
else if (position == 1) {
if (this.exibirFull) {
retorno = VIEW_TYPE_INFO_LIST;
}
else {
retorno = VIEW_TYPE_GALERIA;
}
}
else {
retorno = VIEW_TYPE_GALERIA;
}
}
return retorno;
}
#Override
public int getViewTypeCount() {
return 4;
}
public void updateJsonPerfil(List<JSONObject> json) {
this.jsonList = json;
this.notifyDataSetChanged();
}
#Override
public View getView(int i, View container, ViewGroup viewGroup) {
this.viewHolder = null;
int type = this.getItemViewType(i);
if (container == null) {
this.viewHolder = new ViewHolder();
switch (type) {
case VIEW_TYPE_FOTO_PRINCIPAL:
container = this.activity.getLayoutInflater().inflate(R.layout.perfil_foto, viewGroup, false);
this.viewHolder.imageView = (ImageView) container.findViewById(R.id.perfil_foto_imageView);
break;
case VIEW_TYPE_INFO:
container = this.activity.getLayoutInflater().inflate(R.layout.perfil_info, viewGroup, false);
this.viewHolder.textViewApelido = (TextView) container.findViewById(R.id.perfil_info_apelido);
this.viewHolder.textViewCidade = (TextView) container.findViewById(R.id.perfil_info_textVewCidade);
this.viewHolder.textViewDistancia = (TextView) container.findViewById(R.id.perfil_info_textViewDistancia);
break;
case VIEW_TYPE_INFO_LIST:
container = this.activity.getLayoutInflater().inflate(R.layout.perfil_info_list, viewGroup, false);
this.viewHolder.listViewInfo = (ListView) container.findViewById(R.id.perfil_info_list_listView);
break;
case VIEW_TYPE_GALERIA:
container = this.activity.getLayoutInflater().inflate(R.layout.perfil_info, viewGroup, false);
break;
}
container.setTag(this.viewHolder);
}
else {
this.viewHolder = (ViewHolder)container.getTag();
}
if (this.jsonList.size() > 0) {
JSONObject json = this.jsonList.get(i);
try {
if (type == VIEW_TYPE_FOTO_PRINCIPAL) {
JSONObject foto = json.getJSONObject("foto");
this.imageLoader.displayImage(foto.getString("full"), this.viewHolder.imageView);
}
else if (type == VIEW_TYPE_INFO) {
JSONObject perfil = json.getJSONObject("perfil");
this.viewHolder.textViewApelido.setText(perfil.getString("apelido"));
this.viewHolder.textViewCidade.setText(perfil.getString("cidade"));
this.viewHolder.textViewDistancia.setText(perfil.getString("distancia"));
}
else if (type == VIEW_TYPE_INFO_LIST) {
// This is where i use the second ListView
this.viewHolder.listViewInfo.setAdapter(new PerfilInfoAdapter(this.activity, json.getJSONArray("info")));
}
else {
Log.d("PerfilAdapter", "Populando: VIEW_TYPE_GALERIA");
}
} catch (JSONException e) {
e.printStackTrace();
}
}
return container;
}
protected class ViewHolder {
ImageView imageView;
TextView textViewApelido;
TextView textViewCidade;
TextView textViewDistancia;
ListView listViewInfo;
}
}
I have tried changing the ListView to a GridView, but the problem is with the adapter.
Can anybody help me? I would appreciate it!
Your first problem: Trying to combine a ListView and a GridView. Never ever ever put one within the other. You'll have all sorts of problems...as you are noticing. The first big problem is scrolling. Android does not like it when a scrollable view is embedded within another and both scroll the same direction. Comical sketch from Android employee about this. Doing so is only viable when one scrolls horizontally and the other vertically.
Your next big problem, embedding a ListAdapter within another ListAdapter. You have to remember, that the getView() method can be invoked 3-4 times per position. When you embed another adapter for each position which itself will be invoked 3-4 times per it's own position...holy performance hit! This has bad idea written all over it.
A concern I see is your JSONArray/List referencing. The PerfilInfoAdapter maintains the same reference to the JSONArray used to instantiate it...which is the same data referenced by the PerfilAdapter List. Further the PerfilAdapter maintains the same list referenced by whomever is using it. This sets up a dangerous chain of references that can cause issues when modifying if you are not careful. Ideally, each adapter should maintain the same data in its on List or JSONArray instance.
To sum up, the answer is to change your design choice. There are other ways to display data other then needing vertical scrolling within vertical scrolling. If the ListView doesn't need scrolling use a LinearLayout. If the GridView doesn't need scrolling use a TableLayout or GridLayout. Or just completely change the UX by coming up with a different UI.
As a side note, if you need a full fledged JSONArray adapter check out Advanced-Adapters. The JSONAdapter is releasing within a week or so and can be found on the Redesign branch. Its code complete, just the demo app that's holding up the release.
How about implement getItem correctly (not return null)?
or I prefer
make POJO class and convert JSON into it. (like using GSON)
extends ArrayAdapter instead of BaseAdapter so that u don't have to implements all abstract methods.
I have searched between many notifyDataSetChanged() issue questions without finding my specific case, so here it is:
Problem
I have a root thread (started by the UI thread) that listen for something.
Everytime it receives a message, it starts a new thread (let's call them children threads) that does some operations and, at the end of its life, notifies to the UI adapter that an object has been added.
This procedure works 99.99% of the time (I have stressed a lot the program) but in some cases that I cannot understand this notification does not work.
I am sure that the problem is the listview because the two above statements (setImageBitmap) work properly, changing the imageViews images.
Code
Handler initialization in the Activity and passed to the class that works with threads
//class scope variable
private final Handler mHandler = new Handler(new IncomingHandlerCallback(this));
//At the end of the activity class
/**
* ref. https://groups.google.com/forum/#!msg/android-developers/1aPZXZG6kWk/lIYDavGYn5UJ
*/
class IncomingHandlerCallback implements Handler.Callback {
Activity activity;
public IncomingHandlerCallback(Activity activity) {
this.activity = activity;
}
#SuppressWarnings("unchecked")
#Override
public boolean handleMessage(Message msg) {
DeviceUtils.hideKeyboard(activity);
switch (msg.what) {
case EXECUTE_CODE_UPDATE_TCP_COUNT:
updateDebugCounter(true);
break;
.
.
.
case EXECUTE_CODE_UPDATE_LIST_COUNT:
updateDebugCounter(false);
break;
}
return true;
}
}
Like the comment says that declaration is taken here
Custom Array Adapter
public class PlateInfoListAdapter extends ArrayAdapter<PlateInfo> {
private final Activity context;
CheckBox selectAll;
List<PlateInfo> plateList;
AnprInterface anprInterface;
private MobileANPRDetailPopup readingDetailPopup;
public PlateInfoListAdapter(Activity context, List<PlateInfo> plateList, CheckBox selectAll, AnprInterface anprInterface) {
super(context, R.layout.adapter_plate_list, 0,
plateList);
this.context = context;
this.selectAll = selectAll;
this.plateList = plateList;
this.anprInterface = anprInterface;
final LayoutInflater factory = context.getLayoutInflater();
readingDetailPopup = new MobileANPRDetailPopup(context, factory.inflate(R.layout.popup_mobile_anpr_reading_detail, null));
readingDetailPopup.setBackgroundDrawable(Reso.getDrawable(context, R.drawable.grey_border_white_bck));
readingDetailPopup.setOutsideTouchable(true);
readingDetailPopup.update();
}
static class ViewHolder {
protected LinearLayout layout;
protected CheckBox checkBox;
protected TextView date;
protected TextView plate;
protected TextView plateCountry;
protected ImageView blackList;
protected ImageView whiteList;
protected ImageButton readingDetail;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = null;
PlateInfo plateInfo = plateList.get(position);
boolean hotlistDrawn = false;
if(plateList.size() == 1) {
selectAll.setVisibility(View.VISIBLE);
}
if (convertView == null) {
LayoutInflater inflator = context.getLayoutInflater();
view = inflator.inflate(R.layout.adapter_plate_list, null);
final ViewHolder viewHolder = new ViewHolder();
viewHolder.layout = (LinearLayout) view.findViewById(R.id.ll_plate_layout);
viewHolder.checkBox = (CheckBox) view.findViewById(R.id.cb_plate);
viewHolder.plate = (TextView) view.findViewById(R.id.tv_plate_plate);
viewHolder.plateCountry = (TextView) view.findViewById(R.id.tv_plate_country);
viewHolder.date = (TextView) view.findViewById(R.id.tv_plate_data);
viewHolder.blackList = (ImageView) view.findViewById(R.id.iv_plate_blacklist);
viewHolder.whiteList = (ImageView) view.findViewById(R.id.iv_plate_whitelist);
viewHolder.checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// Here we get the position that we have set for the checkbox using setTag.
int getPosition = (Integer) buttonView.getTag();
// Set the value of checkbox to maintain its state.
plateList.get(getPosition).setRowChecked(buttonView.isChecked());
}
});
viewHolder.readingDetail = (ImageButton) view.findViewById(R.id.ib_plate_detail);
viewHolder.readingDetail.setFocusable(false);
viewHolder.readingDetail.setFocusableInTouchMode(false);
viewHolder.readingDetail.setTag(plateInfo);
viewHolder.readingDetail.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if(anprInterface.isReading()) {
return;
}
PlateInfo selectedPlateInfo = (PlateInfo) viewHolder.readingDetail.getTag();
if(selectedPlateInfo != null) {
readingDetailPopup.showCentered(v, selectedPlateInfo);
}
}
});
view.setTag(viewHolder);
} else {
view = convertView;
}
ViewHolder holder = (ViewHolder) view.getTag();
// Restore the checked state properly
holder.checkBox.setTag(position);
holder.checkBox.setChecked(plateInfo.isRowChecked());
holder.plate.setText(plateInfo.getPlate());
holder.plateCountry.setText(plateInfo.getPlateCountry());
holder.date.setText(DateUtils.formatHelianDateToCustomHumanDate("HH:mm:ss dd-MM-yyyy", plateInfo.getDate()));
holder.readingDetail.setTag(plateInfo);
if(plateInfo.getBlackWhiteListNote() != null && !plateInfo.getBlackWhiteListNote().equals("")) {
if(plateInfo.getBlackWhiteListNote().contains("WHITELIST")) {
holder.layout.setBackgroundColor(Reso.getColor(context, R.color.red_light));
holder.whiteList.setVisibility(View.VISIBLE);
hotlistDrawn = true;
}
else {
holder.whiteList.setVisibility(View.INVISIBLE);
}
if(plateInfo.getBlackWhiteListNote().contains("BLACKLIST")) {
holder.layout.setBackgroundColor(Reso.getColor(context, R.color.red_light));
holder.blackList.setVisibility(View.VISIBLE);
hotlistDrawn = true;
}
else {
holder.blackList.setVisibility(View.INVISIBLE);
}
}
else {
holder.layout.setBackgroundColor(Reso.getColor(context, R.color.transparent));
holder.whiteList.setVisibility(View.INVISIBLE);
holder.blackList.setVisibility(View.INVISIBLE);
}
if(plateInfo.isRowSelected()) {
if(hotlistDrawn) {
holder.layout.setBackgroundDrawable(Reso.getDrawable(context, R.drawable.list_selector_red_bck_blue_border_normal));
}
else {
holder.layout.setBackgroundDrawable(Reso.getDrawable(context, R.drawable.list_selector_blue_border_normal));
}
}
else if(hotlistDrawn) {
holder.layout.setBackgroundColor(Reso.getColor(context, R.color.red_light));
}
else {
holder.layout.setBackgroundColor(Reso.getColor(context, R.color.transparent));
}
return view;
}
#Override
public int getCount() {
return plateList.size();
}
#Override
public PlateInfo getItem(int position) {
return plateList.get(position);
}
#Override
public boolean hasStableIds() {
return true;
}
}
The following code is called by the children threads.
Here it is the code that generates the issue:
mHandler.post(new Runnable() {
#Override
public void run() {
mLastPlateImage.setImageBitmap(plateImage);
mLastContextImage.setImageBitmap(contextImageResized);
mPlateInfoList.add(0, plateInfo);
// That is the problem
mPlateListAdapter.notifyDataSetChanged();
System.gc();
}
});
I got a list, that i would like to show in a ListView, using my own custom adapter. So this list have some null values in it, and also some "important" not-null values.
It could be like this:
(N representing Null)
(D representint some valuable data)
myList:
[N,N,D,D,N,N,D,N,D,N,N] for example.
So i got my adapter, but i cannot handle the null values in the list.
This is my adapter:
public class ItemAdapter extends ArrayAdapter {
List<Item> itemList;
private Activity act;
boolean selling;
public ItemAdapter(Activity act, List<Item> itemList, boolean selling) {
super(act, R.layout.item_view_layout, itemList);
this.itemList = itemList;
this.act = act;
this.selling = selling;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
final Item itemManipulated = itemList.get(position);
if (convertView == null) {
convertView = new ItemInShopView(act, itemManipulated);
holder = new ViewHolder();
holder.convertView = convertView;
holder.itemNameTextView = (TextView) ((ItemView) convertView).getItemNameTextView();
holder.iconImageView = (ImageView) ((ItemView) convertView).getItemIconImageView();
holder.coinView = (CoinView) ((ItemInShopView) convertView).getCoinView();
holder.coinView.init(act);
holder.itemRarityHidedTextView = (TextView) ((ItemView) convertView).getItemRarityHidedTextView();
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.itemNameTextView.setText(itemManipulated.getName());
holder.iconImageView.setImageResource(itemManipulated.getIcon());
holder.itemRarityHidedTextView.setText(itemManipulated.getRarity());
Colorer.setTextViewColorByItemRarity(holder.itemNameTextView, holder.getRarity(), act);
if (selling) {
holder.coinView.setCoins(itemManipulated.getSellPrice());
} else {
holder.coinView.setCoins(itemManipulated.getPrice());
}
holder.convertView.setOnClickListener(new OnClickListenerWithPreventDoubleTapper(itemManipulated));
holder.convertView.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
return false;
}
});
return convertView;
}
class OnClickListenerWithPreventDoubleTapper extends OnClickListenerWithPreventDoubleTap {
Item item;
public OnClickListenerWithPreventDoubleTapper(Item item) {
this.item = item;
}
#Override
protected void performAction() {
new ItemDialog(act, item).show();
}
}
static class ViewHolder {
TextView itemRarityHidedTextView;
TextView itemNameTextView;
ImageView iconImageView;
CoinView coinView;
View convertView;
public String getRarity() {
return itemRarityHidedTextView.getText().toString();
}
}
}
How could i implement some way to get the adapter handle null values and shows nothing there, or maybe show a dummy layout with nothing on it?
I couldnt make a new list without null values and pass these new list to the adapter, because the nulls have an important meaning in an other view.
I hope my problem is clear. Please help if you can.
Why not just?
if (itemManipulated != null){
holder.itemNameTextView.setText(itemManipulated.getName());
holder.iconImageView.setImageResource(itemManipulated.getIcon());
holder.itemRarityHidedTextView.setText(itemManipulated.getRarity());
Colorer.setTextViewColorByItemRarity(holder.itemNameTextView, holder.getRarity(),act);
if (selling) {
holder.coinView.setCoins(itemManipulated.getSellPrice());
} else {
holder.coinView.setCoins(itemManipulated.getPrice());
}
} else {
holder.itemNameTextView.setText("No Item");
... (whatever default values you like)
}
Edit:
If you don't want to display nulls at all you will need to do something more complex.
in getView: find the nth piece of actual data
in getCount: return the number of pieces of actual data
getView:
int posInArray = -1;
for (int i =0; i <= position; i++){
posInArray++;
while(itemList.get(posInArray) == null){
posInArray++;
}
}
itemManipulated = itemList.get(posInArray);
...
getCount:
int count = 0;
for(int i = 0; i < itemList.size(); i++){
if (itemList.get(i) != null){
count++;
}
}
return count;
I am looking to build something similar to the settings UI of system android. I want something like a few checkboxpreferences, switchpreferences, edittextpreferences on the launch of application and then when user selects one preference open a fragment but i am just not able to figure that out.
I have referred Settings guide but it insists on using preference header. While displaying headers there is an unlikely overhead i am facing of displaying texts which in turn will load fragments.
For example,
My preference header is something like :
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- These settings headers are only used on tablets. -->
<header
android:fragment="${packageName}.${activityClass}$GeneralPreferenceFragment"
android:title="#string/pref_header_general" />
<header
android:fragment="${packageName}.${activityClass}$NotificationPreferenceFragment"
android:title="#string/pref_header_notifications" />
<header
android:fragment="${packageName}.${activityClass}$DataSyncPreferenceFragment"
android:title="#string/pref_header_data_sync" />
</preference-headers>
and just to load the actual data, i am having to use it. The actual data will have checkboxes and edittexts.
It would be great if someone gave some insights on this. It would be of great help if i could launch the actual fragment data on loading of screen. Better if i could have control of what fragment to call and call other fragments when a fragment item is selected.
To create custom preference headers, with switches and such, you need to extend PreferenceActivity with Headers as the Android docs describe and then override PreferenceActivity.setListAdapter to create your own list adapter, which creates the custom views. I made a pastebin with the code from the actual android settings activity to help you out. http://pastebin.com/RhSndGCQ
#Override
public void onBuildHeaders(List<Header> headers) {
loadHeadersFromResource(R.xml.settings_headers, headers);
updateHeaderList(headers);
}
#Override
public void setListAdapter(ListAdapter adapter) {
if (adapter == null) {
super.setListAdapter(null);
} else {
super.setListAdapter(new HeaderAdapter(this, getHeaders(), mAuthenticatorHelper));
}
}
private static class HeaderAdapter extends ArrayAdapter<Header> {
static final int HEADER_TYPE_CATEGORY = 0;
static final int HEADER_TYPE_NORMAL = 1;
static final int HEADER_TYPE_SWITCH = 2;
private static final int HEADER_TYPE_COUNT = HEADER_TYPE_SWITCH + 1;
private final WifiEnabler mWifiEnabler;
private final BluetoothEnabler mBluetoothEnabler;
private final ProfileEnabler mProfileEnabler;
private AuthenticatorHelper mAuthHelper;
private static class HeaderViewHolder {
ImageView icon;
TextView title;
TextView summary;
Switch switch_;
}
private LayoutInflater mInflater;
static int getHeaderType(Header header) {
if (header.fragment == null && header.intent == null) {
return HEADER_TYPE_CATEGORY;
} else if (header.id == R.id.wifi_settings
|| header.id == R.id.bluetooth_settings
|| header.id == R.id.profiles_settings) {
return HEADER_TYPE_SWITCH;
} else {
return HEADER_TYPE_NORMAL;
}
}
#Override
public int getItemViewType(int position) {
Header header = getItem(position);
return getHeaderType(header);
}
#Override
public boolean areAllItemsEnabled() {
return false; // because of categories
}
#Override
public boolean isEnabled(int position) {
return getItemViewType(position) != HEADER_TYPE_CATEGORY;
}
#Override
public int getViewTypeCount() {
return HEADER_TYPE_COUNT;
}
#Override
public boolean hasStableIds() {
return true;
}
public HeaderAdapter(Context context, List<Header> objects,
AuthenticatorHelper authenticatorHelper) {
super(context, 0, objects);
mAuthHelper = authenticatorHelper;
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// Temp Switches provided as placeholder until the adapter replaces these with actual
// Switches inflated from their layouts. Must be done before adapter is set in super
mWifiEnabler = new WifiEnabler(context, new Switch(context));
mBluetoothEnabler = new BluetoothEnabler(context, new Switch(context));
mProfileEnabler = new ProfileEnabler(context, null, new Switch(context));
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
HeaderViewHolder holder;
Header header = getItem(position);
int headerType = getHeaderType(header);
View view = null;
if (convertView == null || headerType == HEADER_TYPE_SWITCH) {
holder = new HeaderViewHolder();
switch (headerType) {
case HEADER_TYPE_CATEGORY:
view = new TextView(getContext(), null,
android.R.attr.listSeparatorTextViewStyle);
holder.title = (TextView) view;
break;
case HEADER_TYPE_SWITCH:
view = mInflater.inflate(R.layout.preference_header_switch_item, parent,
false);
holder.icon = (ImageView) view.findViewById(R.id.icon);
holder.title = (TextView)
view.findViewById(com.android.internal.R.id.title);
holder.summary = (TextView)
view.findViewById(com.android.internal.R.id.summary);
holder.switch_ = (Switch) view.findViewById(R.id.switchWidget);
break;
case HEADER_TYPE_NORMAL:
view = mInflater.inflate(
R.layout.preference_header_item, parent,
false);
holder.icon = (ImageView) view.findViewById(R.id.icon);
holder.title = (TextView)
view.findViewById(com.android.internal.R.id.title);
holder.summary = (TextView)
view.findViewById(com.android.internal.R.id.summary);
break;
}
view.setTag(holder);
} else {
view = convertView;
holder = (HeaderViewHolder) view.getTag();
}
// All view fields must be updated every time, because the view may be recycled
switch (headerType) {
case HEADER_TYPE_CATEGORY:
holder.title.setText(header.getTitle(getContext().getResources()));
break;
case HEADER_TYPE_SWITCH:
// Would need a different treatment if the main menu had more switches
if (header.id == R.id.wifi_settings) {
mWifiEnabler.setSwitch(holder.switch_);
} else if (header.id == R.id.bluetooth_settings) {
mBluetoothEnabler.setSwitch(holder.switch_);
} else if (header.id == R.id.profiles_settings) {
mProfileEnabler.setSwitch(holder.switch_);
}
// No break, fall through on purpose to update common fields
//$FALL-THROUGH$
case HEADER_TYPE_NORMAL:
if (header.extras != null
&& header.extras.containsKey(ManageAccountsSettings.KEY_ACCOUNT_TYPE)) {
String accType = header.extras.getString(
ManageAccountsSettings.KEY_ACCOUNT_TYPE);
ViewGroup.LayoutParams lp = holder.icon.getLayoutParams();
lp.width = getContext().getResources().getDimensionPixelSize(
R.dimen.header_icon_width);
lp.height = lp.width;
holder.icon.setLayoutParams(lp);
Drawable icon = mAuthHelper.getDrawableForType(getContext(), accType);
holder.icon.setImageDrawable(icon);
} 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);
}
break;
}
return view;
}
public void resume() {
mWifiEnabler.resume();
mBluetoothEnabler.resume();
mProfileEnabler.resume();
}
public void pause() {
mWifiEnabler.pause();
mBluetoothEnabler.pause();
mProfileEnabler.pause();
}
}