Android Basic Training Course: Rating Bar in ListView Android

    RatingBar is an extension of SeekBar and ProgressBar that shows a rating by stars. It is an a hard to customizing widget in Android. Specially, using it in ListView (each row has a RatingBar ones) even harder because of the Recycling of AdapterView.
    By this post, I would like to present a sample project about this RatingBar matter, hope that can be useful for caching the RatingBar status when integrating in ListView. See this DEMO VIDEO first:

Customizing a ListView adapter

    Like another previous example, I custom a ListView adapter based on ArrayAdapter, using a ViewHolder class to make ListView scrolling smoother. But first, declaring each row layout in clude a RatingBar and TextView simple like this:
    Set max stars for Rating bar though android:numStars property and set rating step by android:stepSize.
    In adapter code, we should override the getView() method and inflate this xml layout with every ListView item!
    The most important step is handling the RatingBar event invoking by user, we will update item ListView value (rating stars) by it. So, provide this OnRatingBarChangeListener method:
 private RatingBar.OnRatingBarChangeListener onRatingChangedListener(final ViewHolder holder, final int position) {
        return new RatingBar.OnRatingBarChangeListener() {
            @Override
            public void onRatingChanged(RatingBar ratingBar, float v, boolean b) {
                Movie item = getItem(position);
                item.setRatingStar(v);
            }
        };
    }
    In getView(), using it by set this code to RatingBar:
holder.ratingBar.setOnRatingBarChangeListener(onRatingChangedListener(holder, position));
    And now we have full adapter code:
package info.devexchanges.ratingbarlistview;

import android.app.Activity;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.RatingBar;
import android.widget.TextView;

import java.util.List;

public class ListViewAdapter extends ArrayAdapter<Movie> {

    private AppCompatActivity activity;
    private List<Movie> movieList;

    public ListViewAdapter(AppCompatActivity context, int resource, List<Movie> objects) {
        super(context, resource, objects);
        this.activity = context;
        this.movieList = objects;
    }

    @Override
    public Movie getItem(int position) {
        return movieList.get(position);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);

        if (convertView == null) {
            convertView = inflater.inflate(R.layout.item_listview, parent, false);
            holder = new ViewHolder(convertView);
            convertView.setTag(holder);

        } else {
            holder = (ViewHolder) convertView.getTag();
            //holder.ratingBar.getTag(position);
        }

        holder.ratingBar.setOnRatingBarChangeListener(onRatingChangedListener(holder, position));

        holder.ratingBar.setTag(position);
        holder.ratingBar.setRating(getItem(position).getRatingStar());
        holder.movieName.setText(getItem(position).getName());

        return convertView;
    }

    private RatingBar.OnRatingBarChangeListener onRatingChangedListener(final ViewHolder holder, final int position) {
        return new RatingBar.OnRatingBarChangeListener() {
            @Override
            public void onRatingChanged(RatingBar ratingBar, float v, boolean b) {
                Movie item = getItem(position);
                item.setRatingStar(v);
                Log.i("Adapter", "star: " + v);
            }
        };
    }

    private static class ViewHolder {
        private RatingBar ratingBar;
        private TextView movieName;

        public ViewHolder(View view) {
            ratingBar = (RatingBar) view.findViewById(R.id.rate_img);
            movieName = (TextView) view.findViewById(R.id.text);
        }
    }
}

Activity coding

    Maybe nothing special in our activity code, locate all view from layout and set adapter for ListView, handling items click (showing a details Dialog) we have this code:
package info.devexchanges.ratingbarlistview;

import android.annotation.SuppressLint;
import android.app.Dialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private ListView listView;
    private ArrayAdapter<Movie> adapter;
    private ArrayList<Movie> arrayList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (ListView)findViewById(R.id.list_view);
        setLisData();
        adapter = new ListViewAdapter(this, R.layout.item_listview, arrayList);
        listView.setAdapter(adapter);

        listView.setOnItemClickListener(onItemClickListener());
    }

    private AdapterView.OnItemClickListener onItemClickListener() {
        return new AdapterView.OnItemClickListener() {
            @SuppressLint("SetTextI18n")
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                final Dialog dialog = new Dialog(MainActivity.this);
                dialog.setContentView(R.layout.layout_dialog);
                dialog.setTitle("Movie details");

                TextView name = (TextView) dialog.findViewById(R.id.movie_name);
                TextView country = (TextView) dialog.findViewById(R.id.country);
                TextView starRate = (TextView) dialog.findViewById(R.id.rate);

                Movie movie = (Movie) parent.getAdapter().getItem(position);
                name.setText("Movie name: " + movie.getName());
                country.setText("Producing country: " + movie.getCountry());
                starRate.setText("Your rate: " + movie.getRatingStar());

                dialog.show();
            }
        };
    }

    private void setLisData() {
        arrayList = new ArrayList<>();
        arrayList.add(new Movie(1, "Avenger 2: Age of Ultron", "USA"));
        arrayList.add(new Movie(1, "Ant man", "USA"));
        arrayList.add(new Movie(1, "Ted 2", "USA"));
        arrayList.add(new Movie(1, "Cities In Love", "China"));
        arrayList.add(new Movie(1, "Attack on Titan 2: End of the World", "Japan"));
        arrayList.add(new Movie(1, "Dragon Ball Z: The Fall of Men", "France"));
        arrayList.add(new Movie(1, "Hitman: Agent 47", "USA"));
        arrayList.add(new Movie(1, "Criminal Activities", "USA"));
        arrayList.add(new Movie(1, "The Witness", "China"));
        arrayList.add(new Movie(1, "Paris Holiday ", "HongKong"));
        arrayList.add(new Movie(1, "The Beauty Inside", "Korea"));
        arrayList.add(new Movie(1, "Phantom", "USA"));
        arrayList.add(new Movie(1, "Youth", "France"));
        arrayList.add(new Movie(1, "The Third Way Of Love", "China"));
        arrayList.add(new Movie(1, "Maze Runner 2: The Scorch Trials", "USA"));
    }
}

Styling RatingBar

    The default RatingBar look not well and we should customizing it. So, in res/styles.xml, provide a new style for it:
    Ofcourse, also putting the selector drawables for it (by create new drawable resource in res/drawable folder):
    Declaring to use this style for RatingBar in main activity layout:
    And output will be like it:

The model (POJO) of this project:
package info.devexchanges.ratingbarlistview;

public class Movie {

    private float ratingStar;
    private String name;
    private String country;

    public Movie(int ratingStar, String name, String country) {
        this.ratingStar = ratingStar;
        this.name = name;
        this.country = country;
    }

    public float getRatingStar() {
        return ratingStar;
    }

    public void setRatingStar(float ratingStar) {
        this.ratingStar = ratingStar;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }
}

Conclusions

    In my opinion, RatingBar is worst widget in Android - it's hard to set style and the default interface is so ugly. Though this post, I hope can provide a simple - not well - way to custom it. By further, you can learn how to use it on ListView, especially how to save its state! By searching on Internet, we can find out that many tuts about this problem!


Share


Previous post
« Prev Post
Next post
Next Post »