Change projections to display KPIs in your app

We can use a different projection and the same observer pattern for displaying some KPIs. Actually that is pretty easy, as we will see in this recipe.

Getting ready

For this recipe, you need to have completed the previous one successfully.

How to do it...

We will continue working on the app from the previous recipe and we will add a new view to display the KPIs:

  1. Open the project you have worked on in the previous recipe.
  2. Add a new layout, fragment_thoughts_kpi.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:gravity="center_horizontal"   
      android:padding="16dp"
      android:layout_height="match_parent">
      <TextView
            android:id="@+id/thoughts_kpi_count"          
            android:textSize="32sp"
            android:layout_margin="16dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <TextView
            android:id="@+id/thoughts_kpi_avg_happiness"
            android:text= "@string/average_happiness"
            android:textSize="32sp"
            android:layout_margin="16dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <RatingBar
            android:id="@+id/thoughts_rating_bar_happy"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:clickable="false"
            android:numStars="5"
            android:rating="0" />
    </LinearLayout>
    
  3. Add a new fragment and name it ThoughtsKpiFragment. It descends from the Fragment class. We will be using the LoaderManager here as well so it will basically look like this:
    public class ThoughtsKpiFragment extends Fragment    
     implements LoaderManager.LoaderCallbacks<Cursor> {
       @Override
        public Loader<Cursor> onCreateLoader(int id, Bundle args) {return null;
        }
        @Override
    	public void onLoadFinished(Loader<Cursor> loader, Cursordata) {
        }
        @Override
        public void onLoaderReset(Loader<Cursor> loader) {
        }
    }
  4. Because we will be using two loaders to display two different KPIs, we are going to add two constant values first:
    public static int LOADER_COUNT_THOUGHTS = 1;
    public static int LOADER_AVG_RATING = 2;
  5. Override the onCreate method:
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate( 
         R.layout.fragment_thoughts_kpi, container, false);
        getKpis();
        return view;
    }
  6. Create the getKpis method (where we initialize the loader twice for different purposes):
    private void getKpis(){
        getLoaderManager().initLoader(LOADER_COUNT_THOUGHTS, null, 
         this);
        getLoaderManager().initLoader(LOADER_AVG_RATING, null, 
         this); 
    }
  7. Add the implementation for the onCreateLoader method. This time the projection depends on the id of the loader. The projection is just like you would expect it to be if it was plain SQL. We are counting the number of rows and we are calculating the average happiness:
    @Override 
     public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        if (id == LOADER_COUNT_THOUGHTS) {
          String[] projection = new String[] {"COUNT(*) AS kpi"};
          android.content.CursorLoader cursorLoader = new android.content.CursorLoader(getActivity(),  
            ThoughtsProvider.CONTENT_URI, projection, null, null, 
             null);
          return cursorLoader;
        }
        else {
          String[] projection = new String[]
             {"AVG(happiness) AS kpi"};
          android.content.CursorLoader cursorLoader = new 
          android.content.CursorLoader(getActivity(), 
           ThoughtsProvider.CONTENT_URI, projection, null, null, 
            null);
          return cursorLoader;}
    }
  8. Once the data comes in, we arrive at the onLoadFinished method and will call methods to display the data, if there is any:
    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        if (data == null || !data.moveToNext()) {
            return;
        }
        if (loader.getId() == LOADER_COUNT_THOUGHTS) {
            setCountedThoughts(data.getInt(0)); 
        }
        else{
            setAvgHappiness(data.getFloat(0));
        }
    }
  9. Add the setCountedThoughts and setAvgHappiness methods. If the fragment is still attached to the activity, we will update the text view or the rating bar:
    private void setCountedThoughts(final int counted){
        if (getActivity()==null){
            return;
        }
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
              TextView countText = (TextView)getView().findViewById(
                 R.id.thoughts_kpi_count);
              countText.setText(String.valueOf(counted));
            }
        });
    }
    private void setAvgHappiness(final float avg){
        if (getActivity()==null){
            return;
        }
        getActivity().runOnUiThread(new Runnable() {
            @Override
    		public void run() {
                RatingBar ratingBar =        
                 (RatingBar)getView().findViewById(
                  R.id.thoughts_rating_bar_happy);
                ratingBar.setRating(avg);}
        });
    }
  10. In the MainActivity file, add a private member for the KPI fragment:
    private ThoughtsKpiFragment mThoughtsKpiFragment;
  11. Create a method getKpiFragment:
    private ThoughtsKpiFragment getKpiFragment(){
        if (mThoughtsKpiFragment==null){
            mThoughtsKpiFragment = new ThoughtsKpiFragment();
        }
        return mThoughtsKpiFragment;
    }
  12. Locate the onNavigationDraweItemSelected method and add this to the if statement:
    … 
    else if (position==0){ 
        fragmentManager.beginTransaction()
                .replace(R.id.container, getKpiFragment())
                .commit();
    }

Run your app. Now we have some neat statistics in our thoughts app:

How to do it...

In this and in the previous recipe, we have seen how easy working with data becomes once you have grokked the concept of content providers.

So far we did all this within the same app; however, since we are already prepared to export the content provider, let us find out how to read our thoughts in a different app. Let's do that now.

See also

Refer Chapter 5, Size Does Matter

Refer Chapter 8, Improving Quality

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset