AutoCompleteTextView 1. add buttons, 2. create contact adapter, 3. spaces in AutoCompleteTextView - android

i am trying to make a Compose New Message screen like the Messaging app built-in in Android 4.1
i noticed that when a person selects a contact, there is a Button sort of thing in the AutoCompleteTextView inserted. HOW is that possible? Please help, as I do not even have any idea to give a starting code for it.
P.S. : I would LOVE to implement something like in the "Tags" entry in StackOverflow when creating a new post. I.E. the words (matched contacts) being replaced by buttons, with a little X on the right edge to remove them! :-)
how do you create the adapter which loads so fast? one idea would be to cache a List of all contacts (names, phone no.s, phone no. types). any other ideas ?
(like, if we could query the content provider with an initial search string of say 2 characters, which would reduce the no. of autocomplete results tremenduously, thus reducing the time taken to load the adapter. but of course, this would require to set the adapter for the autocompletetextview as each character is typed. My doubt is that, can the ContactsContract be queried for search results starting with a few given letters, rather than just scanning the entire database from the starting using a Cursor?)
the autocompletetextview does not recognize spaces, and just gives no results.
i have already read this but could not implement it, does anyone have any working code for this?

You free to look at source code of built-in messaging app at Android GitHub. All you need is patience to read and understand code. And i remind you that not all of code are in public SDK so careful in usage.
1, The MultiAutoComplete with Contact tag RecipientEditTextView. And RecipientsEditor is class extended RecipientEditTextView they use it in app
Update : Here is layout of Compose screen. Line 37 is the autocompletetextview you want. And below is its layout.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<com.android.mms.ui.RecipientsEditor
android:id="#+id/recipients_editor"
android:hint="#string/to_hint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textFilter"
android:layout_weight="1"
android:textColor="#000000"
/>
<ImageButton android:id="#+id/recipients_picker"
android:src="#drawable/ic_launcher_contacts"
android:layout_marginLeft="5dip"
android:layout_width="50dip"
android:layout_height="fill_parent"
android:layout_weight="0"
android:visibility="gone"
/>

I built TokenAutoComplete to solve this issue as there did not appear to be a simple way to do this. Here's a basic example solving points 1 and 3:
public class ContactsCompletionView extends TokenCompleteTextView {
public ContactsCompletionView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected View getViewForObject(Object object) {
Person p = (Person)object;
LayoutInflater l = (LayoutInflater)getContext().getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
LinearLayout view = (LinearLayout)l.inflate(R.layout.contact_token, (ViewGroup)ContactsCompletionView.this.getParent(), false);
((TextView)view.findViewById(R.id.name)).setText(p.getName());
return view;
}
#Override
protected Object defaultObject(String completionText) {
//Stupid simple example of guessing if we have an email or not
int index = completionText.indexOf('#');
if (index == -1) {
return new Person(completionText, completionText.replace(" ", "") + "#example.com");
} else {
return new Person(completionText.substring(0, index), completionText);
}
}
}
Layout code for contact_token (you'll need to find your own x drawable)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:background="#drawable/token_background">
<TextView android:id="#+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#android:color/white"
android:textSize="14sp"
android:text="Test Me"
android:padding="2dp" />
<ImageView
android:layout_height="10dp"
android:layout_width="10dp"
android:src="#drawable/x"
android:layout_gravity="center_vertical"
android:layout_marginLeft="3dp"
android:layout_marginRight="5dp" />
</LinearLayout>
Token backgound drawable
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="#ffafafaf" />
<corners
android:topLeftRadius="5dp"
android:bottomLeftRadius="5dp"
android:topRightRadius="5dp"
android:bottomRightRadius="5dp" />
</shape>
Person object code
public class Person implements Serializable {
private String name;
private String email;
public Person(String n, String e) { name = n; email = e; }
public String getName() { return name; }
public String getEmail() { return email; }
#Override
public String toString() { return name; }
}
Sample activity
public class TokenActivity extends Activity {
ContactsCompletionView completionView;
Person[] people;
ArrayAdapter<Person> adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
people = new Person[]{
new Person("Marshall Weir", "marshall#example.com"),
new Person("Margaret Smith", "margaret#example.com"),
new Person("Max Jordan", "max#example.com"),
new Person("Meg Peterson", "meg#example.com"),
new Person("Amanda Johnson", "amanda#example.com"),
new Person("Terry Anderson", "terry#example.com")
};
adapter = new ArrayAdapter<Person>(this, android.R.layout.simple_list_item_1, people);
completionView = (ContactsCompletionView)findViewById(R.id.searchView);
completionView.setAdapter(adapter);
completionView.setTokenClickStyle(TokenCompleteTextView.TokenClickStyle.Delete);
}
}
Layout code
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.tokenautocomplete.ContactsCompletionView
android:id="#+id/searchView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
Here's what it looks like:

Related

Why is my AutoCompleteTextView not working?

This feels strange as normally the widgets work as they are supposed to. I have an AutoCompleteTextView that I want to populate with a list of city names. It seems simple but doesn't work as I intend to. Here is the resulting output:
Here is the layout in picture:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity1">
<AutoCompleteTextView
android:id="#+id/autocomptv_city_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp"
android:background="#drawable/bg_edittext_rect_opd"
android:hint="Select City"
android:text=""
android:inputType="text"
android:maxLines="1"
android:completionThreshold="0"
android:padding="10dp"
android:singleLine="true"
android:textColor="#000000"
android:textSize="15sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
And below is the java code for the same:
public class MainActivity1 extends AppCompatActivity {
AutoCompleteTextView mAutCompleteTextViewSelfCity;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main1);
mAutCompleteTextViewSelfCity = ((AutoCompleteTextView) findViewById(R.id.autocomptv_city_list));
setupCityDropdownwidget();
}
private void setupCityDropdownwidget() {
Type listType = new TypeToken<List<CityName>>() {}.getType();
List<CityName> citiesList = Singletons.getGsonInstance().fromJson(TestData.cityDataJson, listType);
CityArrayAdapter adapter = new CityArrayAdapter(this, R.layout.item_spinner_city, citiesList);
mAutCompleteTextViewSelfCity.setAdapter(adapter);
adapter.notifyDataSetChanged();
mAutCompleteTextViewSelfCity.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
CityName selectedCitySelf = ((CityName) parent.getItemAtPosition(position));
mAutCompleteTextViewSelfCity.setText(selectedCitySelf.getCityName());
}
});
}
}
Problem:
Well, I want the view to be such that as soon as the user taps on it, it shows a dropdown of cities and when the user starts typing their city for filtering, the view keeps showing narrowed down suggestions for the same.
Currently, the only time it is able to suggest is when I type in something and empty out the text view. If I change the completion threshold to 1, no suggestions are shown ever.
What's wrong with my code?
Here is the complete source for reference: https://wetransfer.com/downloads/ce4017f5f2488288ef7494dc029e033420191019092536/7afa9a3e64afb257293533bd634d6c3220191019092536/dc2341
So ultimately, it turned out to be about the basics - The data item that ArrayAdapter works with, should provide a meaningful toString() override. That is what had been missing from my implementation.
From the docs:
By default, the array adapter creates a view by calling
Object#toString() on each data object in the collection you provide,
and places the result in a TextView.
I did end up wasting up some time but the experience and knowledge would surely come in handy some day.
YOu don't need to set adapter.notifyDataSetChanged();, just set your adapter and everything goes fine.

Select item from list like facebook/hangouts friend selection android [duplicate]

I am not sure I am using the right words for this UI feature, but I have attached a snapshot of what I am looking to achieve in my app.
Its used by Go SMS, where a user types contacts in an edit text, after the user selects a contact from the completion drop down, the contact is inserted in the edit text as show in the attached image.The edit text is still open to accept further input.
For my app, I would like to do the grouping and insertion as soon as the user enters a comma, Just like the Tag input for StackOverflow works (But I believe I can handle that alone.) My problem is what kind of View is this or how do I modify an EditText to behave like this?
Thanks.
The official Chips library from Google (as used in Gmail, Email, Calendar, Messaging) is located at https://android.googlesource.com/platform/frameworks/opt/chips/
A simple example of how to use it can be found at https://code.google.com/p/platform-features-talk-io-2013/source/browse/src/com/example/iotalk/ChipsActivity.java
Two more Chips libraries.
Android Chips. Unlike some other this one is updated to have visuals reflecting the newly released "Material Design" standard.
Token Auto Complete. It is an Android Gmail style token auto-complete text field and filter.
Update:
Official way of doing it is now through Chips material component.
https://material.io/components/chips/
https://material.io/develop/android/components/chip/
https://medium.com/material-design-in-action/chips-material-components-for-android-46001664a40f
Old Answer:
You are looking for Chips library.
Here is one https://github.com/kpbird/chips-edittext-library
Another library that works with contacts by Roman Nurik https://plus.google.com/+RomanNurik/posts/WUd7GrfZfiZ
I think we can build our own chips view with Recycler view and Edit text or Auto complete text view. So we can customize it easily.
1. Created a tag shape say, tags_layout.xml in Drawable
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#cfcfcf">
</solid>
<corners android:radius="20dp">
</corners>
2. Created a layout for recycler view
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:layout_margin="4dp"
android:gravity="center"
android:background="#drawable/tags_layout">
<TextView
android:id="#+id/tag_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:maxLength="25"
android:ellipsize="end"
android:padding="2dp"
android:text="Hello"/>
<ImageView
android:id="#+id/tag_closeBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_close"/>
3. In our activity layout, we implement widgets recycler view just above edit text to keeping tags and edit text or Autocomplete text view to enter tags.
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="#+id/tagsRecyclerView"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</android.support.v7.widget.RecyclerView>
<EditText
android:id="#+id/tagsEditText"
android:inputType="text"
android:imeOptions="actionDone"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
4. Created a model java class for recycler view
public class RecyclerModel {
private String tagText;
public RecyclerModel(String tagText){
this.tagText = tagText;
}
public String getTagText() {
return tagText;
}
public void setTagText(String tagText) {
this.tagText = tagText;
}
}
5. Adapter class for recycler view
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.RecyclerAdapterHolder> {
Context context;
ArrayList<RecyclerModel> model = new ArrayList<>( );
public RecyclerAdapter(Context context,ArrayList<RecyclerModel> model){
this.context = context;
this.model = model;
}
#NonNull
#Override
public RecyclerAdapterHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recycler_layout, parent, false);
return new RecyclerAdapterHolder(itemView);
}
#Override
public void onBindViewHolder(final RecyclerAdapterHolder holder, final int position) {
final RecyclerModel mod = model.get( position );
holder.tagTextView.setText( mod.getTagText() );
//remove tag on click x button
holder.tagImageView.setOnClickListener( new View.OnClickListener() {
#Override
public void onClick(View v) {
model.remove( position );
notifyDataSetChanged();
}
} );
}
#Override
public int getItemCount() {
return model.size();
}
public static class RecyclerAdapterHolder extends RecyclerView.ViewHolder {
public TextView tagTextView;
public ImageView tagImageView;
public RecyclerAdapterHolder(View itemView) {
super( itemView );
tagTextView = itemView.findViewById( R.id.tag_textView );
tagImageView = itemView.findViewById( R.id.tag_closeBtn );
}
}
}
6. Finally, In our activity, add data to recycler on entering data in edit text
public class MainActivity extends AppCompatActivity {
RecyclerView tagsRecyclerView;
EditText tagsEditText;
ArrayList<RecyclerModel> recyclerModels = new ArrayList<>( );
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_main );
tagsRecyclerView = findViewById( R.id.tagsRecyclerView );
tagsEditText = findViewById( R.id.tagsEditText );
tagsEditText.setOnEditorActionListener( new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
Toast.makeText( MainActivity.this,"hello",Toast.LENGTH_SHORT );
String str = tagsEditText.getText().toString();
if(str != null && !str.equals( "" )) {
getUpdateData( str );
tagsEditText.setText( null );
RecyclerAdapter adapter = new RecyclerAdapter( MainActivity.this, recyclerModels );
FlexboxLayoutManager gridLayout = new FlexboxLayoutManager( MainActivity.this );
tagsRecyclerView.setLayoutManager( gridLayout );
tagsRecyclerView.setAdapter( adapter );
}
}
return false;
}
} );
}
private void getUpdateData(String str) {
RecyclerModel model = new RecyclerModel( str );
recyclerModels.add( model );
}
}
7. Manifest file should contain gradles
implementation 'com.android.support:recyclerview-v7:27.1.1'
implementation 'com.google.android:flexbox:1.0.0'
there is a new library for Android Material Chips!
Starting from android support library version 28.0.0 Google added Chip view that allows us to display a chip view within our layout.
Design and documentation about Chip
And simple example:
<android.support.design.chip.ChipGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
app:chipSpacing="8dp">
<android.support.design.chip.Chip
android:id="#+id/some_chip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Android Chip Group"
app:chipIcon="#drawable/ic_android"
app:closeIconVisible="true" />
<android.support.design.chip.Chip
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Android"
app:chipIcon="#drawable/ic_android" />
<android.support.design.chip.Chip
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Chip"
app:chipIcon="#drawable/ic_android" />
<android.support.design.chip.Chip
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Group"
app:chipIcon="#drawable/ic_android" />
</android.support.design.chip.ChipGroup>
A lot has changed. we have new libraries. I would recommend this library. It is very easy and powerful.
Simply add this dependency
implementation "com.hootsuite.android:nachos:1.1.1"
and this view
<com.hootsuite.nachos.NachoTextView
android:id="#+id/nacho_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:chipHorizontalSpacing="2dp"
app:chipBackground="#color/chip_background"
app:chipTextColor="#color/cheddar"
app:chipTextSize="16dp"
app:chipHeight="30dp"
app:chipVerticalSpacing="3dp"/>
and this adapter:
val suggestions = arrayOf("Tortilla Chips", "Melted Cheese", "Salsa", "Guacamole", "Mexico", "Jalapeno")
val adapter = ArrayAdapter(context, android.R.layout.simple_dropdown_item_1line, suggestions)
nachoTextView.setAdapter(adapter)

New Game activity in Android: what UI choices?

My Android game project needs a "New Game" activity. The game has a couple of options that should be asked each time a new game is started: the board size and the game seed. I would like to do this in a form resembling the standard "Settings" activity, but with an added "Start" button at the bottom. Given that these things will be chosen every new game, they don't want to be hidden away in the app's Settings activity. So what's the best way to go about this?
Things I've investigated:
Using a custom PreferenceActivity, since the behaviour is almost the same. Since the PreferenceActivity doesn't use layout XML files I can't see a way of adding a button to it.
A Linear layout that includes a ListView and a Button. I've started implementing this, but I can't even find a way to add items to the ListView - the ListView guide in the documentation certainly isn't aimed at a beginner like me! And then the list items need to be interactive to allow options to be selected: this looks like a lot of work to duplicate what PreferenceActivity almost does on its own.
Is there an option that I'm missing? I've spent a couple of hours trying to find an answer in the documentation and in previous questions, but with no progress. If anyone can point me in the right direction I'd be grateful.
Here is some code for you. Hope it will be useful.
You should go by the second way and use LinearLayout (this is main.xml):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ListView
android:id="#+id/lvSimple"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>
<Button // describe your start button here/>
</LinearLayout>
Layout for ListView item:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:id="#+id/ivImg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_launcher">
</ImageView>
<LinearLayout
android:id="#+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<CheckBox
android:id="#+id/cbChecked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CheckBox">
</CheckBox>
<TextView
android:id="#+id/tvText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginRight="20dp"
android:text="TextView">
</TextView>
</LinearLayout>
</LinearLayout>
Your MainActivity.java:
public class MainActivity extends Activity {
// имена атрибутов для Map
final String ATTRIBUTE_NAME_TEXT = "text";
final String ATTRIBUTE_NAME_CHECKED = "checked";
final String ATTRIBUTE_NAME_IMAGE = "image";
ListView lvSimple;
/** Called when the activity is first created. */
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// data array
String[] texts = { "sometext 1", "sometext 2", "sometext 3",
"sometext 4", "sometext 5" };
boolean[] checked = { true, false, false, true, false };
int img = R.drawable.ic_launcher;
// our data source
ArrayList<Map<String, Object>> data = new ArrayList<Map<String, Object>>(
texts.length);
Map<String, Object> m;
for (int i = 0; i < texts.length; i++) {
m = new HashMap<String, Object>();
m.put(ATTRIBUTE_NAME_TEXT, texts[i]);
m.put(ATTRIBUTE_NAME_CHECKED, checked[i]);
m.put(ATTRIBUTE_NAME_IMAGE, img);
data.add(m);
}
String[] from = { ATTRIBUTE_NAME_TEXT, ATTRIBUTE_NAME_CHECKED,
ATTRIBUTE_NAME_IMAGE };
// array of IDs of Views
int[] to = { R.id.tvText, R.id.cbChecked, R.id.ivImg };
// create adapter
SimpleAdapter sAdapter = new SimpleAdapter(this, data, R.layout.item,
from, to);
// create ListView and set the adapter
lvSimple = (ListView) findViewById(R.id.lvSimple);
lvSimple.setAdapter(sAdapter);
}
}

Changing width of individual items in a listview

So I've searched around for an answer or a possible solution to this problem for a couple weeks now and still haven't gotten any farther. I'm working on building an app that rewards users with points after certain things. One of the pages they can visit is a leader board based off of the friends the user has.
I'm able to implement the leader board and print the users in order based off of their points, but can't implement a bar graph style look. Like So: http://imgur.com/tF51RsA
(Had to post a link because I can't paste a picture in here)
Here is what I've tried so far:
1. Adding a to xml and trying to getLayoutParams in the custom adapter then set the width, which didn't work.
2. using onDraw to draw a rectangle over top of the list item.
Here is my Leader board xml file (or close to it):
<LinearLayout
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="#+id/leader_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="left|top"
android:background="#00000000">
</LinearLayout>
My listview row xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/frame"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:id="#+id/bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/user_image"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_centerVertical="true"
android:background="#drawable/ic_default_user"/>
<TextView
android:id="#+id/rank"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:paddingLeft="10dp"
android:layout_toRightOf="#+id/user_image"/>
<TextView
android:id="#+id/user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:paddingLeft="20dp"
android:textSize="16sp"
android:layout_toRightOf="#+id/rank"/>
<TextView
android:id="#+id/score"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_toRightOf="#+id/user_name"
android:gravity="right"
android:paddingRight="15dp"/>
</RelativeLayout>
</FrameLayout>
and my custom ArrayAdapter in my leaderboard class
public class LeaderAdapter extends ArrayAdapter<LeaderboardDM>{
ArrayList<LeaderboardDM> leaders;
int layoutResourceId;
public LeaderAdapter(Context context, int layoutResourceId, ArrayList<LeaderboardDM> leaders){
super(context, layoutResourceId, leaders);
this.leaders = new ArrayList<LeaderboardDM>();
this.leaders = leaders;
this.layoutResourceId = layoutResourceId;
}
public View getView(int position, View convertView, ViewGroup parent){
View v = convertView;
ViewHolder viewHolder = null;
if(v == null){
v = getLayoutInflater().inflate(layoutResourceId, null, false);
viewHolder = new ViewHolder();
viewHolder.userImage = (ImageView) v.findViewById(R.id.user_image);
viewHolder.rank = (TextView) v.findViewById(R.id.rank);
viewHolder.userName = (TextView) v.findViewById(R.id.user_name);
viewHolder.score = (TextView) v.findViewById(R.id.score);
v.setTag(viewHolder);
}else{
viewHolder = (ViewHolder) v.getTag();
}
LeaderboardDM lead = leaders.get(position);
if(lead != null){
//doesn't set user image yet
viewHolder.userName.setText(lead.user);
viewHolder.score.setText(String.valueOf(lead.points));
viewHolder.rank.setText("#"+String.valueOf(position+1));
}
return v;
}
class ViewHolder{
ImageView userImage;
TextView rank, userName, score;
}
}
and the leaderboardDM class
public class LeaderboardDM{
public String user;
public int points;
public String profilePicUrl;
public void setUserName(String user){
this.user = user;
}
public String getUserName(){
return user;
}
public void setPoints(int points){
this.points = points;
}
public int getPoints(){
return points;
}
public void setProfilePic(String url){
this.profilePicUrl = url;
}
public String getProfilePicUrl(){
return profilePicUrl;
}
}
The list gets sorted through using a comparator and again prints in order of their score. If anyone has an idea on how to create something like this please help point me in the right direction.
Thanks!
Edit:
Thanks to #Ridcully I was able to solve this issue.
The solution worked perfectly for anyone in the future that may do this, was to create a custom progress bar and get the drawable from the resources then set the ProgressDrawable to that drawable. From there I would set the width by setProgress(int width).
You could use a RelativeLayout and put a ProgressBar (with customized drawable) in the back. This way you can set the width of the bar by a simple setProgress().

Understanding memory management when navigating inside Activities

I have setup a little example application where the idea is to navigate from one Activity to another and to study Memory consumption because I don't really understand when/where memory is released during this process.
Idea is to create an Activity which consume quite a lot of memory to see if memory is released correctly when we leave it before recreating it.
A HomeActivity only composed of a Button that call a BlogListActivity when button is clicked.
BlogListActivity is a ListActivity that contain BlogPost objects. This BlogPost contains a Bitmap in order to use some memory.
List of BlogPost is created dynamically in the onCreate method of BlogListActivity and then passed to an Adapter to display each PostBlog object in row of my ListView.
On an emulator with Android 2.3.3 and 128Mo of memory, I manage to move from HomeActivity to BlogListActivity and then come back to HomeActivity two times. On the third try, I get an OutOfMemoryError from BitmapFactory.
This mean I have a Memory Leak: objects that are not used anymore but still have a reference on it so they are not released. But I don't where I do it wrong.
Can someone help me finding it.
Thanks in advance for your help.
Bertrand
Link to complete source code and Eclipse project
Here is an extract of the code we are interested in
HomeActivity source code
public class HomeActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
}
public void onSecondActivityClick(View v) {
startActivity(new Intent(this, BlogListActivity.class));
}
}
BlogListActivity source code
public class BlogListActivity extends ListActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bloglist);
List<BlogPost> items = new ArrayList<BlogPost>();
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.mn);
for (int i = 0; i < 5; i++) {
BlogPost post = new BlogPost();
post.author = String.format("Author%d", i);
post.title = String.format("Title%d", i);
post.date = new Date();
post.imageURL = "https://si3.twimg.com/profile_images/1143791319/MN_BLEU.png";
post.image = bmp;
post.image = BitmapFactory.decodeResource(getResources(), R.drawable.mn);
items.add(post);
}
setListAdapter(new LazyArrayAdapter(this, R.layout.listitem_blog, items));
}
}
LazyArrayAdapter source code
public class LazyArrayAdapter extends ArrayAdapter<BlogPost> {
public LazyArrayAdapter(Context context, int textViewResourceId, List<BlogPost> objects) {
super(context, textViewResourceId, objects);
}
#Override
public View getView(int index, View view, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
if (view == null) {
view = inflater.inflate(R.layout.listitem_blog, parent, false);
}
TextView title = (TextView)view.findViewById(R.id.listitemblog_title);
TextView date = (TextView)view.findViewById(R.id.listitemblog_date);
ImageView icon = (ImageView)view.findViewById(R.id.listitemblog_icon);
BlogPost post = this.getItem(index);
title.setText(post.title);
date.setText(new SimpleDateFormat().format(post.date));
icon.setImageBitmap(post.image);
return view;
}
}
BlogPost source code
public class BlogPost {
public String title;
public String author;
public Date date;
public String imageURL;
public Bitmap image;
}
activity_bloglist Layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#android:id/list">
</ListView>
</LinearLayout>
ListItemBlog Layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:weightSum="100"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout android:layout_width="0px" android:layout_weight="70"
android:id="#+id/linearLayout2"
android:orientation="vertical"
android:layout_height="fill_parent">
<TextView android:id="#+id/listitemblog_title"
android:layout_width="wrap_content"
android:text="TextView"
android:textStyle="bold"
android:layout_height="wrap_content">
</TextView>
<TextView
android:id="#+id/listitemblog_date"
android:layout_width="wrap_content"
android:text="TextView"
android:layout_height="wrap_content"
android:textStyle="bold">
</TextView>
</LinearLayout>
<ImageView
android:id="#+id/listitemblog_icon"
android:layout_width="0px"
android:scaleType="centerInside"
android:layout_weight="30"
android:src="#drawable/icon"
android:layout_height="fill_parent"/>
</LinearLayout>
HomeActivity layout:
<?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">
<Button
android:layout_height="wrap_content"
android:onClick="onSecondActivityClick"
android:layout_width="wrap_content"
android:id="#+id/button1"
android:text="Button">
</Button>
</LinearLayout>
I have studied memory usage with DDMS + MAT. Here are screenshots of what I see in MAT for the com.webcontentlistview I create:
Memory usage after navigating to BlogListActivity one time
Memory usage after navigatin to BlogListActivity several times
As we can see, even after navigating between both Activity, we still have only one BlogListActivity object in memory (with it's associated content).
But numbers of java and android objects are increasing (lines 2 and 3).
Could it be that the garbage collector simply does not have time to clean your data before you launch the activity again? How quickly did you do the test? Does it always crash, even if you take some time between starting BlogListActivity? Maybe try to run System.gc() each time the app returns to HomeActivity and see if the crashes resume.

Categories

Resources