简体   繁体   中英

a “List of Favorites” in android - re-change the rating bar

I would like to make a list of favorites in my app. I am using arrayadapter.

I have a string, and a rating bar, of one star only! I want that if you press the star once, it lights the star (and make it favorite) and when you press it again, the star isnt filled... (and now its not favorite anymore)

what should I write here? and is it even correct this if quesion?

public class ChooseFavorites extends Activity implements OnItemClickListener
{
    String[] stations;
    float[] rating;
    boolean[] ifCheacked;
    RatingAdapter ratingAdapter; 

ArrayAdapter<String> arr;
ListView list;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_choose_favorites);
    stations =  getResources().getStringArray(R.array.stations);
    rating = new float[stations.length];
    ifCheacked = new boolean[stations.length];
    Arrays.fill(ifCheacked, false);
    ratingAdapter = new RatingAdapter(this, R.layout.favorite_row, stations);
    list = (ListView)findViewById(R.id.stationsList1);
    list.setAdapter(ratingAdapter);
    list.setOnItemClickListener(this);

}




class RatingAdapter extends ArrayAdapter<String> 
{
    Context ctx;
    LayoutInflater inflater;
    TextView stationName;
    RatingBar star;


    public RatingAdapter(Context context, int textViewResourceId, String[] objects) 
    {
        super(context, textViewResourceId, objects);
        ctx = context;
        inflater = LayoutInflater.from(context);
    }



@Override
public View getView(int position, View convertView, ViewGroup parent) 
{
    View row = convertView;
    if(row==null)
    { // Object reuse
        inflater = getLayoutInflater();
        row = inflater.inflate(R.layout.favorite_row, parent, false);
    }
    stationName = (TextView)row.findViewById(R.id.textFavoriteItemInRow);
    stationName.setText(stations[position]);
    star= (RatingBar)row.findViewById(R.id.ratingBar1);
    if (!ifCheacked[position]) //ifChecked is false
    {
        star.setRating(rating[position]);
        star.setTag(position);
        ifCheacked[position] = true;
    }
    else
    {


    }
    star.setOnRatingBarChangeListener(new RatingBar.OnRatingBarChangeListener()
    {
        public void onRatingChanged(RatingBar ratingBar, float rating2, boolean fromUser)
        {
            if(!fromUser) return;
            int index = (Integer)(ratingBar.getTag());
            rating[index] = rating2;
        }
    });
    return row;
}


    }

    @Override
    public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
        // TODO Auto-generated method stub

    }
}

Probably you should not use a RatingBar , but a CheckBox instead. I'm using the CheckBox in one of my apps exactly as you would need it.

Basically what you've need to do is the following:

In your res\\drawable\\ folder, define a new layout file, suppose you call it checkboxes.xml .

In that file, you would define something like this:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:state_checked="true" android:drawable="@drawable/checkbox_star_down" />
  <item android:state_checked="false" android:drawable="@drawable/checkbox_star" />
</selector>

In my case, both drawables are two JPG images, one with the star shining and one turned off.

Now in your row layout, define the CheckBox instead your RatingBar , with something like this:

<CheckBox
   android:id="@+id/soloR"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_weight="0.6"
   android:onClick="somethingChanged"
   android:textSize="12sp"
   android:textColor="#FFFFFF"
   android:button="@drawable/checkboxes"
   android:text="@string/soloR" />

Nice, you might probably seen the android:button property set to that checkboxes you just defined. Once done that, remove your star.setOnRatingBarChangeListener(new RatingBar.OnRatingBarChangeListener() ... implementation, and replace it by:

onCheckedChangeListener (CompoundButton.OnCheckedChangeListener listener) 

The listener of onCheckedChanged provides two parameters: CompoundButton buttonView, boolean isChecked .

Basically, the second one tells you whether your star is shining or not. The rest of your code seems generally to be reasonable but:

  • I wouldn't do the row = convertView assignation. That's a practice I've seen a lot of times and it makes no sense, work directly on the convertView instance.

  • I don't know how many rows are you working within your ListView , but if they are too much or they're hard to render, I'd consider using viewHolder s. If you haven't worked with them, here's a nice link which explains this greatly.

  • I see you're using findViewById() on each row. That might lead to huge performance problems, as this call is highly expensive. I'd save both drawables (assuming your star may just have 2 stated) within your class and assign them in the constructor, so you process them just for once - or as I said previously, use viewHolder s for this.

  • Keep in mind one thing: If you don't save the state of your changes, once your row "disappears" from the screen (because the user is scrolling within the list, for instance) and the row is newly rendered, you will see the previous state - ie, the changes won't be saved. For that purpose, it's very important you handle your data within your ArrayAdapter extension, each time the user clicks the star, save its state in your class. I'm meaning the third of your constructor parameters: public RatingAdapter(Context context, int textViewResourceId, String[] objects) . Now you just call super on it, but you don't save it. Keep in mind that this is your working data , any change should be reflected in that Object array, otherwise each time you'll render your rows, changes won't be saved. For instance:

     class RatingAdapter extends ArrayAdapter<String> { Context ctx; LayoutInflater inflater; TextView stationName; RatingBar star; ArrayMap<String, Boolean> rowLayout = ArrayMap<String, Boolean>(); public RatingAdapter(Context context, int textViewResourceId, String[] objects) { super(context, textViewResourceId, objects); ctx = context; inflater = LayoutInflater.from(context); for (String rowText : (String) objects) rowLayout.put(rowText, false); // Supposing all your initial stars will be off } 

    Now all you have to do is to synchronize your getView() method to tell Android how it should be managed. For instance, if the value is false , put the off-star - else, put the shining star, etc.

---- EDIT ----

On how to keep track of the changes: I don't know whether you finally decided using viewHolder s, so I'm going just illustrate the global case.

Everything that changes and you want to keep tracking of should be done within your getView() method. In that method, see that the second parameter is a View called convertView . Speaking very vaguely, this is the layout of every row, ie, all the content of each row is contained in each convertView . So basically, getView() will be called at least once for every row.

Suppose you have a layout where you have a CheckBox whose id is lvCheckBox and a TextView whose id is lvTextView . You can get efficiently any of them by calling, for instance:

TextView tv = (TextView) convertView.findViewById(R.id.lvTextView);

Any listener should also be defined within your getView() method (as already does). From your code you seem to already be clear about things up until now.

Now, how to manage changes: Having in consideration that you need to implement what I formerly posted you about storing a data structure within your ArrayList extension, there are 3 concepts you should be aware of:

  • position : The first of the arguments of getView() is the position of the item that was called for render. But also note that as you defined your listeners within your getView() method, you may also access this parameter and this is crucial.

  • getItem(position) : getItem() is a method from the ArrayAdapter class, which returns an item from the list. Calling this on position will return the item being rendered. It returns the type your ArrayAdapter is, so if you have String s, you'll get String .

  • notifyDataSetChanged() . You have to call this method on your adapter each time the data set changes. The ideal way is doing that within the calling Fragment/Activity, but if you can't (for instance, because you don't know when will it be fired (listeners)), you can get the adapter within your listener and call this method within your listener, this way:

     final ListView lv = (ListView) findViewById(R.id.your_listview)); RatingAdapter adapter = (RatingAdapter) lv.getAdapter(); adapter.notifyDataSetChanged(); 

Knowing all that, there's just one step left: Remove the item from your ArrayAdapter's dataset copy, you'll know it using getItem(position) and call notifyDataSetChanged() on your adapter - from inside if you need to fire it within itself or from your Fragment/Activity otherwise.

The other (probably easier) way is to call .add or .del on your Adapter within your Fragment/Activity, and call notifyDataSetChanged() on it. This is however not always possible.

For instance, I have a ListView with customized layout. This is how I declare it:

private ArrayList<String> elemlist;
elemlist.add("First row");
elemlist.add("Second one");

MultiLayoutArrayAdapter adapter = new MultiLayoutArrayAdapter(MyActivity.this, R.layout.my_custom_layout, elemlist);

I call this within my Activity :

elemlist.add("Yet another row");
adapter.notifyDataSetChanged();

This would appear my row. But sometimes you don't want just to add or delete, but modify: and that's your case (a CheckBox listening to be clicked), so that's when the above comes into action. You'll have to modify the list of items using getItem(position) and removing it from your rowLayout structure, and call notifyDataSetChanged() afterwards from within your onCheckedChangeListener() .

you can done this by checkbox. set two images oncheck(setimage(High lighted star)) and uncheck(setimage(undo image)).

CheckBox fav;
boolean isCheck=false;

if(!isCheck){

fav.setChecked(true);
                    checked.setBackgroundResource(R.drawable.blue_star);
                }else{

                    fav.setBackgroundResource(R.drawable.grey_star);

        fav.setChecked(false);
                }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM