Android Settings UI - android

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();
}
}

Related

Listview add item one at a time

I have a listview and a button in my main activity and three layout ressource files (right.xml, mid.xml and left.xml [They're relative layout]).
I want to make an arrayList (with strings and drawable (images)) and each time I push the button in main.xml the first content of the arrayList will appear at the bottom of the screen (either left, mid or right --> depend of the order of the arrayList) and when I click again the next item (string or drawable) will appear beneath it, pushing it in an upward motion.
UPDATE
I made a Model and an Adapter
Here is the model
public class ModelC1 {
public String C1Name;
public String C1Text;
public int id;
public boolean isSend;
public ModelC1(String C1Name, String C1Text, int id, boolean isSend){
this.id = id;
this.C1Name = C1Name;
this.C1Text = C1Text;
this.isSend = isSend;
}
public int getId(){
return id;
}
public void setId(int id){
this.id = id;
}
public String getC1Name() {
return C1Name;
}
public void setC1Name(String C1Name){
this.C1Name = C1Name;
}
public String getC1Text() {
return C1Text;
}
public void setC1Text (String C1Text){
this.C1Text = C1Text ;
}
public boolean isSend() {
return isSend;
}
public void setIsSend(boolean send){
isSend = send;
}
Here is the Adapter
public class AdapterC1 extends BaseAdapter {
private List<ModelC1> listChat;
private LayoutInflater inflater;
private Context context;
public AdapterC1(List<ModelC1> listChat, Context context){
this.listChat = listChat;
this.context = context;
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return listChat.size();
}
#Override
public Object getItem(int i) {
return listChat.get(i);
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public View getView(int i, View convertView, ViewGroup viewGroup) {
View vi = convertView;
if(convertView == null ){
if(listChat.get(i).isSend() == 0)
vi=inflater.inflate(R.layout.list_send,null);
else if ((listChat.get(i).isSend() == 1))
vi=inflater.inflate(R.layout.list_recv,null);
else if ((listChat.get(i).isSend() == 2))
vi=inflater.inflate(R.layout.list_mid,null);
}else{
if(listChat.get(i).isSend() == 0)
vi=inflater.inflate(R.layout.list_send,null);
else if ((listChat.get(i).isSend() == 1))
vi=inflater.inflate(R.layout.list_recv,null);
else if ((listChat.get(i).isSend() == 2))
vi=inflater.inflate(R.layout.list_mid,null);
}
if(listChat.get(i).isSend() !=0 || listChat.get(i).isSend() !=1 || listChat.get(i).isSend() !=2 ){
BubbleTextView bubbleTextView = (BubbleTextView) vi.findViewById(R.id.bubbleChat);
if(bubbleTextView != null)
bubbleTextView.setText(listChat.get(i).C1Text);
TextView nameTextView = (TextView) vi.findViewById(R.id.nameChat);
if(nameTextView != null)
nameTextView.setText(listChat.get(i).C1Name);
}else{
vi=inflater.inflate(R.layout.list_mid,null);
BubbleTextView bubbleTextView = (BubbleTextView) vi.findViewById(R.id.bubbleChat);
bubbleTextView.setText("THE END");
}
return vi;
}
And here is the activity
public class Chat1 extends AppCompatActivity {
private static final String TAG = "Chat1";
private AdapterC1 adapter;
private List<ModelC1> listChat = new ArrayList<>();
private int count = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat1);
RecyclerView chatContent1 = findViewById(R.id.chatContent1);
}
private ModelC1 setUpMessage(){
Log.d(TAG, "setUpMessage: Exec");
return();
}
///OnClick of the button in the activity_chat1.xml
public void nextClicked1(View view) {
Log.d(TAG, "nextClicked: Is Clicked");
///After the limit of the arraylist is reached
final int limit = 40;
if(count == limit){
Log.d(TAG, "nextClicked: Limit Reached");
Intent i = new Intent(Chat1.this, MainActivity.class);
startActivity(i);
}else{
///Call the list
loadList(null);
}
}
///Load the list of arrays?
public void loadList(View view){
ModelC1 chat = setUpMessage();
listChat.add(chat);
///The ID of the recycleview in the activity_chat1.xml
final RecyclerView recyclerview = findViewById(R.id.chatContent1);
///The adapter
final AdapterC1 adapter = new AdapterC1(listChat, this);
///Make the recyclerview always scroll
///the adapter
///recyclerview.setAdapter(adapter);
}
My questions are now how do I make the ArrayList (containing strings and drawables) and how to link the ArrayList to make it appear one by one when I click on the button ?
As for the ArrayList, will soemthing like that works ?
private List<List<String>> textChat1 = new ArrayList<List<String>>();
ArrayList<String> textChat1 = new ArrayList<String>();
textChat1.add("This is message 1");
textChat1.add("This is message 2");
textChat1.add("This is message 2");
addresses.add(textChat1);
How can I add images and how to say which strings inflate which layout (left, mid or right) ?
You can do your job like this: in your Adapter's getView method ,
#Override
public View getView(int position, View convertView, ViewGroup container) {
if (convertView == null) {
if (position == 1) {
convertView = getLayoutInflater().inflate(R.layout.left, container, false);
} else if (position == 2) {
convertView = getLayoutInflater().inflate(R.layout.mid, container, false);
} else {
convertView = getLayoutInflater().inflate(R.layout.right, container, false);
}
}
//your code here
return convertView;
}
This will do your job, but, I suggest you to use Recyclerview because it's more efficient and better in terms of looks as well as memory management.

How to set a specific row in a modified list view

I have a problem to how can I display a different xml layout for a modified list view.
My list view acts as a comment section.
I just want to display a edit and delete button for the owner of that certain comment and display nothing at all if he/she is not the owner of it.
this is my sample code
btw I am using json
protected void onPostExecute(JSONObject json) {
HashMap<String, String> user = session.sessionGetIdAccountType();
String session_user_id = user.get(UserSessionManager.KEY_ID);
int integer_session_user_id = Integer.parseInt(session_user_id);
String user_id = "";
boolean usercomment = false;
try {
commentData = json.getJSONArray("list");
for (int i = 0; i < commentData.length(); i++) {
JSONObject source = commentData.getJSONObject(i);
Commenters commenter = new Commenters();
commenter.setName(source.getString("User_Fname") + " "
+ source.getString("User_Lname"));
commenter.setDate(source.getString("Comment_Date"));
commenter.setUsername(source.getString("User_Username"));
commenter.setComment(source.getString("Comment_Content"));
int integer_user_id = Integer.parseInt(user_id = source
.getString("User_ID"));
commenterList.add(commenter);
ListView listview = (ListView) findViewById(R.id.comment_list);
if (integer_session_user_id == integer_user_id) {
usercomment = true;
} else {
usercomment = false;
}
if (usercomment) {
adapter2 = new CommenterAdapter2(
getApplicationContext(), R.layout.row_user,
commenterList);
listview.setAdapter(adapter2);
} else {
adapter = new CommenterAdapter(getApplicationContext(),
R.layout.row, commenterList);
listview.setAdapter(adapter);
}
}
}
catch (Exception e) {
e.fillInStackTrace();
}
}
Hope this might help you.
public class TestListViewAdapter extends ArrayAdapter<Object> {
static final int TYPE_HEADER = 0;
static final int TYPE_CELL = 1;
public TestListViewAdapter(Context context, List<Object> objects) {
super(context, 0, objects);
}
#Override
public int getItemViewType(int position) {
switch (position) {
case 0:
return TYPE_HEADER;
default:
return TYPE_CELL;
}
}
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null) {
switch (getItemViewType(position)) {
case TYPE_HEADER: {
convertView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.tools_list_item_card_big, parent, false);
}
break;
case TYPE_CELL: {
convertView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.tools_list_item_card_small, parent, false);
}
break;
}
}
return convertView;
}
}
in getview method of adapter switch case is used for inflating different views on listview row & getItemViewType() returns which view i want to use you can also check this by adding property in array list custom model.

Dynamic ListView issue on Android

Currently I am working on an Android app and use parse as backend where I have created a ListView of nearby places dynamically. But I face the following design issue: When the user clicks on a place, a view must appear under the clicked item.
And I have faced a problem with grouping the place, as you can see in figure 1, there are many branches for HSBC bank. In which case they are under HSBC.
I have tried expandable ListView before, but it does not give much customization for child view , just simple one.
my BaseAdapter:
import java.util.ArrayList;
import java.util.List;
import com.parse.ParseGeoPoint;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewManager;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
public class ListViewAdapter extends BaseAdapter {
// Declare Variables
Context context;
LayoutInflater inflater;
//ImageLoader imageLoader;
private List<AnywallPost> AnywallPostlist = null;
private ArrayList<AnywallPost> arraylist;
public ListViewAdapter(Context context,
List<AnywallPost> AnywallPostlist) {
this.context = context;
this.AnywallPostlist = AnywallPostlist;
inflater = LayoutInflater.from(context);
this.arraylist = new ArrayList<AnywallPost>();
this.arraylist.addAll(AnywallPostlist);
}
public class ViewHolder {
TextView distance;
TextView name;
}
#Override
public int getCount() {
return AnywallPostlist.size();
}
#Override
public Object getItem(int position) {
return AnywallPostlist.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
public View getView(final int position, View view, ViewGroup parent) {
final ViewHolder holder;
final View row=view;
if (view == null) {
holder = new ViewHolder();
view = inflater.inflate(R.layout.user_custom, null);
// Locate the TextViews in listview_item.xml
holder.distance = (TextView) view.findViewById(R.id.disView);
holder.name = (TextView) view.findViewById(R.id.nameView);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
// Set the results into TextViews
holder.name.setText(AnywallPostlist.get(position).getText());
holder.distance.setText(AnywallPostlist.get(position).getDis());
//Listen for ListView Item Click
view.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
Toast.makeText(context,AnywallPostlist.get(position).getText(), Toast.LENGTH_LONG).show();
}
});
return view;
}
}
.
import com.parse.ParseClassName;
import com.parse.ParseGeoPoint;
import com.parse.ParseObject;
import com.parse.ParseQuery;
import com.parse.ParseUser;
/**
* Data model for a post.
*/
#ParseClassName("places")
public class AnywallPost extends ParseObject {
public String getText() {
return getString("text");
}
public void setText(String value) {
put("text", value);
}
public String getBank() {
return getString("bank");
}
public void setBank(String value) {
put("bank", value);
}
public ParseUser getUser() {
return getParseUser("user");
}
public void setUser(ParseUser value) {
put("user", value);
}
public String getType()
{
return getString("type");
}
public void setType(String value)
{
put("type",value);
}
public ParseGeoPoint getLocation() {
return getParseGeoPoint("location");
}
public void setLocation(ParseGeoPoint value) {
put("location", value);
}
public String getDis()
{
return getString("dis");
}
public void setdis(String value)
{
put("dis",value);
}
public static ParseQuery<AnywallPost> getQuery() {
return ParseQuery.getQuery(AnywallPost.class);
}
}
.
switch (getItemViewType(position)) {
case 0:
Bank bank = (Bank) getItem(position);
holder.name.setText(bank.name);
String str = String.valueOf(bank.numBranches);
holder.cn.setText(str);
imageLoader.DisplayImage(bank.image,
holder.logo);
Toast.makeText(getActivity(), "img"+bank.image,Toast.LENGTH_LONG).show();
// ... set the image here
// ... set the number of branches here
break;
case 1:
branch = (AnywallPost) getItem(position);
holder.name.setText(branch.getText());
holder.distance.setText(branch.getDis());
holder.b.setText(branch.getbranch());
imageLoader.DisplayImage(branch.getimg(),
holder.logo);
break;
case 2:
branch = (AnywallPost) getItem(position);
holder.name.setText(branch.getText());
holder.distance.setText(branch.getDis());
holder.b.setText(branch.getbranch());
imageLoader.DisplayImage(branch.getimg(),
holder.logo);
// ... set values for all the expanded view widgets too
break;
}
I have used this code to get image from parse and I add it to AnywallPost
ParseFile image = (ParseFile) country.get("image");
AnywallPost map = new AnywallPost();
map.setimg(image.getUrl());
then, I used this code to load the image and it worked fine
imageLoader.DisplayImage(AnywallPostlist.get(position).getimg(),
holder.logo);
Here is my idea:
You will have two layouts, a brief one with the name & distance, and an expanded one with the name & distance plus all the details.
The brief layout will have a view type of 0, and the expanded layout will have a view type of 1.
When you click on the item, you set a flag in the list model to indicate that the view has switched from brief view to long view. So you should see the view switch. If you click on an expanded view, it will revert to brief view.
I don't have a way to test this, but here is how I think the code will look:
public class ListViewAdapter extends BaseAdapter {
// Declare Variables
Context context;
LayoutInflater inflater;
//ImageLoader imageLoader;
private List<AnywallPost> AnywallPostlist = null;
private ArrayList<AnywallPost> arraylist;
/** this array holds a flag for each item to show if it is in brief view more or expanded view mode*/
private boolean[] expandedView;
public ListViewAdapter(Context context,
List<AnywallPost> AnywallPostlist) {
this.context = context;
this.AnywallPostlist = AnywallPostlist;
inflater = LayoutInflater.from(context);
this.arraylist = new ArrayList<AnywallPost>();
this.arraylist.addAll(AnywallPostlist);
this.expandedView = new boolean[AnywallPostlist.size()];
}
public class ViewHolder {
TextView distance;
TextView name;
// ... add all your expanded view fields here too
}
#Override
public int getCount() {
return AnywallPostlist.size();
}
#Override
public int getViewTypeCount() {
return 2; // brief & expanded
}
#Override
public int getItemViewType(int position) {
return expandedView[position] ? 1 : 0;
}
#Override
public Object getItem(int position) {
return AnywallPostlist.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
public View getView(final int position, View view, ViewGroup parent) {
final ViewHolder holder;
final View row=view;
if (view == null) {
holder = new ViewHolder();
int layout = expandedView[position] ? R.layout.user_custom_expanded : R.layout.user_custom;
view = inflater.inflate(layout, parent, false);
// Locate the TextViews in listview_item.xml
holder.distance = (TextView) view.findViewById(R.id.disView);
holder.name = (TextView) view.findViewById(R.id.nameView);
if (expandedView[position]) {
// ... locate all the expanded view widgets too
}
view.setTag(holder);
} else {
// because the view type is determined by expandedView[position], the correct layout will be recycled here
holder = (ViewHolder) view.getTag();
}
// Set the results into TextViews
holder.name.setText(AnywallPostlist.get(position).getText());
holder.distance.setText(AnywallPostlist.get(position).getDis());
if (expandedView[position]) {
// ... set all the values for your expanded view too
}
//Listen for ListView Item Click
view.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
if (expandedView[position]) {
// this is expanded view so toggle it back to brief view
expandedView[position] = false;
} else {
// clear any expanded views elsewhere
expandedView = new boolean[AnywallPostlist.size()];
// toggle brief view to expanded
expandedView[position] = true;
}
// forces the ListView to refresh and convert the brief view into an expanded view and vice versa.
ListViewAdapter.this.notifyDataSetChanged();
// Toast.makeText(context,AnywallPostlist.get(position).getText(), Toast.LENGTH_LONG).show();
}
});
if (expandedView[position]) {
// ... add all your event listeners for the expanded view widgets
}
return view;
}
}
You can also add a toggle button with expand/collapse indicator to the view to control the expand/collapse so you don't click anywhere on the view to change it.
You can do this with an ExpandableListView if you want, you just need to change to an ExpandableListAdapter and do your layouts a little differently.
Here is some code to make a heterogeneous list:
Take a really good look at the constructor to see how I divided up the branches by bank them strung them all together into one big list.
The list is just an Object array, so you figure out what the item is like this:
If it's a Bank, then well, it's a bank.
If it's an AnywallPost and expandedView[position] == false then it's a branch, brief view
If it's an AnywallPost and expandedView[position] == true then it's a branch, expanded view
public class Bank {
private String name;
private String image; // maybe this needs to be URL?
private int numBranches;
}
public class ListViewAdapter extends BaseAdapter {
// Declare Variables
Context context;
LayoutInflater inflater;
ImageLoader imageLoader;
private Object[] masterList;
/** this array holds a flag for each item to show if it is in brief view more or expanded view mode*/
private boolean[] expandedView;
public ListViewAdapter(Context context,
List<AnywallPost> AnywallPostlist) {
this.context = context;
inflater = LayoutInflater.from(context);
/*
* Phase I -- get list of banks and organize the branches by bank
*/
int count = 0;
List<String bankNames = new ArrayList<String>();
List<Bank> banks = new ArrayList<Bank>();
Map<String, Bank> bankMap = new HashMap<String, Bank>();
Map<String, List<AnywallPost>> mappedBranches = new HashMap<String, List<AnywallPost>>();
Bank bank = null;
List<AnywallPost> branches = null;
for (AnywallPost branch : AnywallPostlist) {
// have we started a branch list for this bank?
if (! bankNames.contains(branch.getBank())) { // no we haven't
// remember the bank name
bankNames.add(branch.getBank());
// init a new Bank
bank = new Bank();
bank.name = branch.getBank();
bank.image = branch.getImg(); // if this is URL make image in Bank a URL
banks.add(bank);
bankMap.put(bank.name, bank);
count++;
// create a list for this bank's branches and index it by bank
branches = new ArrayList<AnywallPost>();
mappedBranches.put(branch.getBank(), branches);
} else { // yes we have
// get the bank for this name
// this fixes the problem with the branch counts
bank = bankMap.get(branch.getBank());
// get the branch list we already created for this bank
branches = mappedBranches.get(branch.getBank());
}
// remember the branch for this bank
branches.add(branch);
count++;
// increment the number of branches for this bank
bank.numBranches++;
}
/*
* Phase II -- sort the banks and branches
*/
// ... here you can order the banks by name, branches by distance, etc.
/*
* Phase III -- create the mixed list
*/
int index = 0;
masterList = new Object[count];
for (Bank bank : banks) {
// append a bank to the list
masterList[index] = bank;
index++;
for (AnywallPost branch : mappedBranches.get(bank.name)) {
// append a branch to the list
masterList[index] = branch;
index++;
}
}
this.expandedView = new boolean[count];
}
public class ViewHolder {
TextView distance;
TextView name;
ImageView logo;
// ... add all your expanded view fields here too
}
#Override
public int getCount() {
return masterList.length;
}
#Override
public int getViewTypeCount() {
return 4; // bank brief, bank expanded, branch brief & branch expanded
}
#Override
public Object getItem(int position) {
return masterList[position];
}
#Override
public int getItemViewType(int position) {
if (getItem(position) instanceof Bank) {
return expandedView[position] ? 1 : 0;
}
return expandedView[position] ? 3 : 2;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View view, ViewGroup parent) {
final ViewHolder holder;
final View row=view;
if (view == null) {
holder = new ViewHolder();
int layout = 0;
switch (getItemViewType(position)) {
case 0:
layout = R.layout.bank_brief;
break;
case 1:
layout = R.layout.bank_expanded;
break;
case 2:
layout = R.layout.branch_brief;
break;
case 3:
layout = R.layout.branch_expanded;
break;
}
view = inflater.inflate(layout, parent, false);
switch (getItemViewType(position)) {
case 0:
holder.name = (TextView) view.findViewById(R.id.nameView);
break;
case 1:
holder.name = (TextView) view.findViewById(R.id.nameView);
// ... locate all the bank expanded view widgets too
break;
case 2:
holder.name = (TextView) view.findViewById(R.id.nameView);
holder.distance = (TextView) view.findViewById(R.id.disView);
break;
case 3:
holder.name = (TextView) view.findViewById(R.id.nameView);
holder.distance = (TextView) view.findViewById(R.id.disView);
// ... locate all the expanded view widgets too
break;
}
view.setTag(holder);
} else {
// because the view type is determined by expandedView[position], the correct layout will be recycled here
holder = (ViewHolder) view.getTag();
}
AnywallPost branch = null;
switch (getItemViewType(position)) {
case 0:
Bank bank = (Bank) getItem(position);
holder.name.setText(bank.name);
imageLoader.DisplayImage(bank.image, holder.logo);
// ... set the number of branches here
break;
case 1:
holder.name.setText(bank.name);
imageLoader.DisplayImage(bank.image, holder.logo);
// ... set the number of branches here
break;
case 1:
branch = (AnywallPost) getItem(position);
holder.name.setText(branch.getText());
holder.distance.setText(branch.getDis());
break;
case 2:
branch = (AnywallPost) getItem(position);
holder.name.setText(branch.getText());
holder.distance.setText(branch.getDis());
// ... set values for all the expanded view widgets too
break;
}
//Listen for ListView Item Click
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
if (expandedView[position]) {
// this is expanded view so toggle it back to brief view
expandedView[position] = false;
} else {
// clear any expanded views elsewhere
expandedView = new boolean[expandedView.length];
// toggle brief view to expanded
expandedView[position] = true;
}
if (getItem(position) instanceof AnywallPost) { // only for branches
// AnywallPost branch = (AnywallPost) getItem(position);
// Toast.makeText(context, branch.getText(), Toast.LENGTH_LONG).show();
}
// forces the ListView to refresh and convert the brief view into an expanded view and vice versa.
notifyDataSetChanged();
}
});
if (expandedView[position]) {
// ... add all your event listeners for the expanded view widgets
}
return view;
}
}

Android BaseAdapter's getCount() getView()

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.

Error while setting adapter for list-view Android

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

Categories

Resources