I'm new to the this excellent site and in the Java programming Android. I started to make a small test application for the listing of my favorite places in my town. I tried to follow some tutorials on different pages, but when I do I put in my project in Eclipse always gives me more than a million mistakes although imports classes and other methods.
I want to build on the example image discotheque next to the image name discos and under that name Diskotek smaller text additional info.
It would be really grateful for all the help
A Custom ListView requires total 4 basic stuff 2 in design (layout .xml) & 2 class (.java)
2 Layout
a) Basically a container which has a listview with either a heading or button depends on you
b) How each row should look like should they have buttons, images, textview how ever you want.
2 Java Class File
a) One is the Activity which you will have definitely.
b) Custom Adapter which says which value from your Activity will go to which View (Button , image ) depending on your requirement.
The best example is to follow this Tutorial
The first thing I think when someone says 'I tried to follow some tutorials...' but they don't work, It gets kind of hard to believe.
Where's the code you tried?
What are the import errors on the editor?
That'd be an easier problem to address.
To give you a simple ListView example:
First, create a resource file at your preference: (example.xml)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/disco_image"
android:layout_alignParentTop="true"
android:src="#drawable/ic_home"
android:layout_width="96dp"
android:layout_height="96dp"/>
<TextView
android:id="#+id/disco_title"
android:padding="12dp"
android:layout_toRightOf="#+id/disco_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="#+id/disco_info"
android:padding="12dp"
android:layout_toRightOf="#+id/disco_image"
android:layout_below="#+id/disco_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</RelativeLayout>
Then, create a custom adapter, they are very simple. Just extend a BaseAdapter for now.
public class ExampleAdapter extends BaseAdapter {
//Let's create some constants first, to fill out the rows
private static final String [] DISCO_NAMES = {"Disco One", "Disco Two", "Disco Three", "Disco Four"};
private static final String [] DISCO_INFO = {"Some Info One", "Some Info Two", "Some Info Three", "Some Info Four"};
private LayoutInflater mInflater;
//Our custom adapter needs a constructor, so you can create from your activity.
public ExampleAdapter (final Context context) {
//for now, let's just get the context, we'll need it to inflate views
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
//this needs to return to the amount of rows you want to display.
//right now we return a fixed value, this could vary based on your needs
return DISCO_NAMES.length;
}
#Override
public Object getItem(int pos) {
//this is useful for knowing what item is at what position
//for now, let's just return the disco name shall we?
return DISCO_NAMES[pos];
}
#Override
public long getItemId(int pos) {
//This returns an id to the item
//personally I don't use this, so you can just return the position
return pos;
}
#Override
public View getView(int position, View view, ViewGroup viewGroup) {
//Ha, here's the important part
//ListViews reuse rows, so let's check if the view (also known as convertview) is new or being reused
if (view == null) {
//this means it's a new view, so we need to inflate it
view = mInflater.inflate(R.layout.example, null);
}
((TextView) view.findViewById(R.id.disco_title)).setText(DISCO_NAMES[position]);
((TextView) view.findViewById(R.id.disco_info)).setText(DISCO_INFO[position]);
//You can also set some images to the imageview on the layout we created earlier
return view;
}
}
Then, let's create a ListActivity for example purposes. NOTE ListActivit does not require a Layout Resource to be set via setContentView, so we don't call it here.
public class ExampleListActivity extends ListActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//create the adapter
ExampleAdapter mExampleAdapter = new ExampleAdapter(this);
//fill the listView
setListAdapter(mExampleAdapter);
}
}
That should compile as is, but you may want to look into ViewHolder patterns and blah blah blah for performance reasons. Obviously you need to read more, but I hope this helps as a starting point.
Related
I want to write an activity that is similar to the about screen of android phones. I want it to display some information in the style of the about screen of android phones.
Like this
title1
info
-----------------
title2
info
-----------------
etc.
Is there a special view that I can use or is it just a result of multiple views placed in a specific way? Or is there an activity template in android studio that I can use?
Use ListView. You can create a custom layout for cells and then use an array or a cursor to fill the data.
ListView: A view that shows items in a vertically scrolling list. The
items come from the ListAdapter associated with this view.
ListAdapter can receive data as input. The adapter would inflate the layout for each cell in its getView() method and assign the data to the individual views in the cell.
Read more about ListView here: http://developer.android.com/reference/android/widget/ListView.html
See PreferenceActivity or PreferenceFragment. They are special list views populated either from code or from a xml file. There are many different preference types to choose from (checkbox, switch, list etc)
An example preference fragment:
You can use ListView and a custom ArrayAdapter to create a screen like that. If you need any help about how to create a custom ArrayAdapter check this useful tutorial here.
If you want to create a simple list, then ListView is probably the simplest option. You may also want to look into ListActivity and/or ListFragment as well to further simplify the process.
If you intend to use complex animations, or have the list update dynamically with animations, you may be better served with RecyclerView, although using it is more complex.
An straightforward implementation of ListActivity could look something like this:
public class MainActivity extends ListActivity {
String[] titles = { "title one", "title two" };
String[] descriptions = { "desc 1", "desc 2" };
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setListAdapter(new ListAdapter() {
leave everything the same, except for getCount() and getView()
#Override
public int getCount() {
return titles.length;
}
This will ensure you list is always the correct length as your array.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) parent.getContext().
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.row, parent, false);
} else {
view = convertView;
}
TextView title = (TextView) view.findViewById(R.id.title);
TextView description = (TextView) view.findViewById(R.id.description);
title.setText(titles[position]);
description.setText(descriptions[position]);
return view;
}
And row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/title"
android:textSize="24sp"
android:textStyle="bold"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/description"
android:textSize="20sp"/>
</LinearLayout>
I know that I should measure children in onMeasure() and layout them in onLayout(). The question is in what of these methods should I add/recycle views so I could measure all children together with an eye on how they are mutually positioned (i.e. grid, list or whatever)?
My first approach was to add/recycle views in onLayout() but from that point I can't measure my children because they aren't added to AdapterView yet and getChildCount() returns 0 in onMeasure(). And I can't measure AdapterView itself without children being already layouted because it really depends upon their mutual positions, right?
I'm really confused with android layouting process in AdapterView when childrens are added/removed dynamically.
I can't post a comment because I'm a new user, but can you describe WHAT you're trying to do, as opposed to HOW you're trying to do it? Frequently, you will find that this is an issue of design as opposed to coding. Especially if you're coming from a different platform (example, iOS). From experience, I found that measuring and manual layouts in Android is mostly unnecessary if you design your layout properly in light of your business need.
EDIT:
As I mentioned this can be solved using some design decisions. I will use your Nodes/List example (hoping that this is your actual use case, but the solution can be expanded for a more general problem).
So if we think about your Header as a comment in a forum, and the List as replies to your comment, we can make the following assumption:
One list is enough, not two. Each item in the list can either be a header (comment) or a list item (reply). Each reply is a comment, but not all comments are replies.
For item n, I know if it's a comment or a reply (i.e. is it a header or an item in your list).
For item n, I have a boolean member isVisible (default false; View.GONE).
Now, you can use the following components:
One extended adapter class
Two Layout XMLs: One for your Comment, one for your reply. You can have unlimited comments and each comment can have unlimited replies. Both those satisfy your requirements.
Your fragment or activity container class that implements OnItemClickListener to show/hide your list.
So let's look at some code, shall we?
First, your XML files:
Comment row (your header)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/overall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true">
<TextView
android:id="#+id/comment_row_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
Now your reply row (an element in your list)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/overall"
android:layout_width="match_parent"
android:layout_height="wrap_content"> <!-- this is important -->
<TextView
android:id="#+id/reply_row_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"/> <!-- important -->
</RelativeLayout>
Ok, now your adapter class
public class CommentsListAdapter extends BaseAdapter implements OnClickListener
{
public static String TAG = "CommentsListAdapter";
private final int NORMAL_COMMENT_TYPE = 0;
private final int REPLY_COMMENT_TYPE = 1;
private Context context = null;
private List<Comment> commentEntries = null;
private LayoutInflater inflater = null;
//All replies are comments, but not all comments are replies. The commentsList includes all your data. (Remember that the refresh method allows you to add items to the list at runtime.
public CommentsListAdapter(Context context, List<Comment> commentsList)
{
super();
this.context = context;
this.inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.commentEntries = commentsList;
}
//For our first XML layout file
public static class CommentViewHolder
{
public RelativeLayout overall;
public TextView label;
}
//For our second XML
public static class ReplyViewHolder
{
public RelativeView replyOverall;
public TextView replyLabel;
}
#Override
public int getViewTypeCount()
{
return 2; //Important. We have two views, Comment and reply.
}
//Change the following method to determine if the current item is a header or a list item.
#Override
public int getItemViewType(int position)
{
int type = -1;
if(commentEntries.get(position).getParentKey() == null)
type = NORMAL_COMMENT_TYPE;
else if(commentEntries.get(position).getParentKey() == 0L)
type = NORMAL_COMMENT_TYPE;
else
type = REPLY_COMMENT_TYPE;
return type;
}
#Override
public int getCount()
{
return this.commentEntries.size(); //all data
}
#Override
public Object getItem(int position)
{
return this.commentEntries.get(position);
}
#Override
public long getItemId(int position)
{
return this.commentEntries.indexOf(this.commentEntries.get(position));
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
CommentViewHolder holder = null;
ReplyViewHolder replyHolder = null;
int type = getItemViewType(position);
if(convertView == null)
{
if(type == NORMAL_COMMENT_TYPE)
{
convertView = inflater.inflate(R.layout.row_comment_entry, null);
holder = new CommentViewHolder();
holder.label =(TextView)convertView.findViewById(R.id.comment_row_label);
convertView.setTag(holder);
}
else if(type == REPLY_COMMENT_TYPE)
{
convertView = inflater.inflate(R.layout.row_comment_reply_entry, null);
replyHolder = new ReplyViewHolder();
replyHolder.replyLable = (TextView)convertView.findViewById(R.id.reply_row_label);
convertView.setTag(replyHolder);
}
}
else
{
if(type == NORMAL_COMMENT_TYPE)
{
holder = (CommentViewHolder)convertView.getTag();
}
else if(type == REPLY_COMMENT_TYPE)
{
replyHolder = (ReplyViewHolder)convertView.getTag();
}
}
//Now, set the values of your labels
if(type == NORMAL_COMMENT_TYPE)
{
holder.label.setTag((Integer)position); //Important for onClick handling
//your data model object
Comment entry = (Comment)getItem(position);
holder.label.setText(entry.getLabel());
}
else if(type == REPLY_COMMENT_TYPE)
{
replyHolder = (ReplyViewHolder)convertView.getTag(); //if you want to implement onClick for list items.
//Or another data model if you decide to use multiple Lists
Comment entry = (Comment)getItem(position);
replyHolder.replyLabel.setText(entry.getLabel()));
//This is the key
if(entry.getVisible() == true)
replyHolder.replyLabel.setVisibility(View.VISIBLE);
else
replyHolder.replyLabel.setVisibility(View.GONE);
}
return convertView;
}
//You can use this method to add items to your list. Remember that if you are using two data models, then you will have to send the correct model list here and create another refresh method for the other list.
public void refresh(List<Comment> commentsList)
{
try
{
this.commentEntries = commentsList;
notifyDataSetChanged();
}
catch(Exception e)
{
e.printStackTrace();
Log.d(TAG, "::Error refreshing comments list.");
}
}
//Utility method to show/hide your list items
public void changeVisibility(int position)
{
if(this.commentEntries == null || this.commentEntries.size() == 0)
return;
Comment parent = (Comment)getItem(position);
for(Comment entry : this.commentEntries)
{
if(entry.getParent().isEqual(parent))
entry.setVisible(!entry.getVisible()); //if it's shown, hide it. Show it otherwise.
}
notifyDataSetChanged(); //redraw
}
}
Ok great, now we have a list of headers with hidden children (remember, we set the default visibility of children to 'gone'). Not what we wanted, so let's fix that.
Your container class (fragment or activity) you will have the following XML definition
<!-- the #null divider means transparent -->
<ListView
android:id="#+id/comments_entries_list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:divider="#null"
android:dividerHeight="5dp" />
And your onCreateView will implement OnItemClickListener and have the following
private ListView commentsListView = null;
private List<Comment>comments = null;
private static CommentsListAdapter adapter = null;
....
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
...
//comments list can be null here, and you can use adapter.refresh(data) to set the data
adapter = new CommentsListAdapter(getActivity(), comments);
this.commentsListView.setAdapter(adapter);
this.commentsListView.setOnClickListener(this); //to show your list
}
Now to show your list when you click a header
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id)
{
adapter.changeVisibility(position);
}
Now, if an item is clicked and that item has a parent (i.e. list item), it will be shown/hidden according to its current state.
Some comments about the code:
I wrote this on WordPad as I don't have a dev environment handy. Sorry for any compilation errors.
This code can be optimized: If you have a very large data set, this code would be slow since you're redrawing the entire list on every call to changeVisibility(). You can maintain two lists (one for headers, one for list items) and in changeVisibility you can query over the list items only).
I re-enforce that idea that some design decisions would make your life a lot easier. For example, if your list items were actually just a list of labels, then you can have one custom XML file (for your header) and a ListView view within it that you can set to View.GONE. This will make all other views pretend that it's not even there and your layout will work properly.
Hope this helps.
I have a GridView adapter based on some example code that I'm trying to figure out. Basically I have an arraylist of applications that I pass into the adapter, the Applications class, among other things, contains the packageinfo for all apps that have been qualified by the user as "Arcade". What I want is to extract the icons from the packageinfo then blow them up and put them into the GridView, no text or anything else underneath, just the app icons.
public class GridAdapter extends BaseAdapter {
private Context mContext;
ArrayList<Applications> mlistArcadeApps;
public GridAdapter(Context context, ArrayList<Applications> listArcadeApps) {
this.mlistArcadeApps = listArcadeApps;
mContext = context;
}
public int getCount() {
return mlistArcadeApps.size();
}
public Object getItem(int position) {
return mlistArcadeApps.get(position);
}
public long getItemId(int position) {
return 0;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView =
}
}
I have that much so far, I'm not entirely sure if this is even right, I'm just trying to get a hold on how to use GridViews, is it possible to extract app icons as an ImageView object? Or is there an alternative method I should look at?
Yes you are doing it right, you need to create another XML file for each item in the list. Create and XML file and put ImageView inside the LinearLayout. After that you need to pass this xml file to your GridView.
Inside your getView method you can update ImageView and show your icons.
This tutorial explains it clearly : Android GridView Layout Tutorial
I just realized, in this tutorial he doesn't create XML file for each item, instead he creates new ImageView at RunTime.
If you look at this example, it shows in different way : Android GridView Example
This is the first time I need to use the ArrayAdapter<T> to show a multi-item Row-Layout. After a lot of successful work with different adapters this one is driving me crazy.
getView(..., position, ...) always returns 0-7[EDIT] so I never see elements in my ArrayList that are on position >= 7[/EDIT]. I know, this is the visible position, but how do I select the correct object in the ArrayList?
EDIT: Currently I only get the first 8 elements out of my array because position only comes in from 0-7 - even on a 50 element ArrayList. I don't see a way to position within the ArrayList without a "real" position.
The docs say the following - but I don't get it. Did somebody successfully implement an ArrayAdapter<T> with a complex layout? What do the doc mean and how should I implement it?
If you want to use a more complex
layout, use the constructors that also
takes a field id. That field id should
reference a TextView in the larger
layout resource. However the TextView
is referenced, it will be filled with
the toString() of each object in the
array. You can add lists or arrays of
custom objects. Override the
toString() method of your objects to
determine what text will be displayed
for the item in the list. To use
something other than TextViews for the
array display, for instance,
ImageViews, or to have some of data
besides toString() results fill the
views, override getView(int, View,
ViewGroup) to return the type of view
you want.
Many thanks in advance
hjw
Here's the code so far:
public class HappyAdapter extends ArrayAdapter<Happy> {
static class ViewHolder {
private ImageView imageView;
private TextView textViewBottom;
private TextView textViewTop;
}
private ArrayList<Happy> arrayListHappy;
private DrawableCache drawableCache = DrawableCache.getInstance();
private int layout;
#Override
public int getCount() {
return arrayListHappy.size();
}
#Override
public View getView(int position, View contentView, ViewGroup viewGroup) {
// position always 0-7
View view = null;
ViewHolder viewHolder = null;
if (contentView == null) {
LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = layoutInflater.inflate(layout, null);
if (view != null) {
viewHolder = new ViewHolder();
viewHolder.imageView = (ImageView) view.findViewById(R.id.happyactivity_row_image_left);
viewHolder.textViewBottom = (TextView) view.findViewById(R.id.happyactivity_row_text_bottom);
viewHolder.textViewTop = (TextView) view.findViewById(R.id.happyactivity_row_text_top);
view.setTag(viewHolder);
}
} else {
view = contentView;
viewHolder = (ViewHolder) contentView.getTag();
}
if (viewHolder != null) {
Happy happy = arrayListHappy.get(position);
if (happy != null) {
viewHolder.imageView.setUrl(happy.getImageThumbnail());
drawableCache.fetchDrawable(happy.getImageThumbnail(), viewHolder.imageView);
viewHolder.textViewBottom.setText(String.valueOf(position));
viewHolder.textViewTop.setText(String.valueOf(viewHolder.position));
}
}
return view;
}
public HappyAdapter(Context context, int layout, ArrayList<Happy> arrayListHappy) {
super(context, layout, arrayListHappy);
this.arrayListHappy = arrayListHappy;
this.layout = layout;
}
}
This is part of the Row-Layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:orientation="horizontal" >
<ImageView
android:id="#+id/happyactivity_row_image_left"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_width="fill_parent" />
<TextView
style="#style/TextViewStandard"
android:id="#+id/happyactivity_row_text_top"
android:layout_weight="1" />
<TextView
style="#style/TextViewStandard"
android:id="#+id/happyactivity_row_text_bottom"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>
I can't add comments yet so have to answer. As others have noted it isn't really clear what's not working. However, you are only seeing positions 0-7 being inflated as this is the only portion of the list that is currently visible, as you noted yourself. Other rows (with higher position numbers) won't be inflated until you scroll down the list.
Although it uses the BaseAdapter class, rather than ArrayAdapter that you are using, you could look at List14.java in the ApiDemos sample code (ApiDemos/src/com/example/android/apis/view/List14.java) as the principle is the same. When using ArrayAdapter though, you don't need to override all the methods that this sample code does (just getView).
I need to create a more advanced list in my android application, that will contain more then just one string per item. Actually, I'm looking for something similar to the appointments view in the calendar app: I want to categories my items by day, and show something similar to the hour of the appointment.
No idea how to go about this though, I'm guessing it's not possible with the ListActivity?
What do you guys suggest?
Edit:
If someone could give a code example I'd really appreciate it, I'm not sure how to execute what was suggested in the current replies..
It's easily doable with ListACtivity. You need to create a subclass within it, that extends ArrayAdapter (or any other list adapter) and overrides it's getView() method. You will want to create an xml file defining the "view" for each list item. Then within your overridden getView() method, you will need to inflate that view, then use findViewById() for each of the elements you want to assign a value to.
public class YourListActivity extends ListActivity {
private String[] values = new String[]{"Row 1", "Row 2", "Row 3"};
private class Adapter extends ArrayAdapter<String> {
private LayoutInflater li = LayoutInflater.from(this.getContext());
public Adapter() {
super(YourListActivity.this, 0, values);
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
View v = li.inflate(R.layout.row, parent);
TextView field1 = (TextView) v.findViewById(R.id.field1);
field1.setText(values[position]);
return v;
}
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setListAdapter(new Adapter());
}
}
row.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/field1"
/>
</LinearLayout>
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ListView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#android:id/list"
/>
</LinearLayout>
You can further modify the appearance of the row, and the fields it holds, by adding additional TextViews to the row.xml, and using the position passed through to getView() to set the values of your extra fields as appropriate, and you can even customise the rows on a row-by-row basis if you need.
This recorded talk from Google I/O last year explains what you want to know: http://www.google.com/events/io/2010/sessions/world-of-listview-android.html
Create a ListAdapter. That's the class responsible for providing the list with its UI elements. With its getView you can create whatever Views you like. If you have more than one type of list element, beware not to reuse the convertView that getView receives.