Tuesday, February 19, 2013

Android PULL TO REFRESH

i just got an idea from some some git-hub resources about the pull-2-refresh implementation, i am going to share the example with you.

Just create and example project and have the main activity with the name

RefreshableListViewActivity .java



package com.jitesh.android;

import java.util.ArrayList;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ArrayAdapter;

import com.jitesh.android.R;
import com.jitesh.android.widget.RefreshableListView;
import com.jitesh.android.widget.RefreshableListView.OnRefreshListener;

public class RefreshableListViewActivity extends Activity {
private ArrayList<String> mItems;
private RefreshableListView mListView;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mItems = new ArrayList<String>();
        for(int i=0;i<10;i++)
        mItems.add("Musethe place "+i);
       

        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, mItems);
        
        mListView = (RefreshableListView) findViewById(R.id.listview);
        mListView.setAdapter(adapter);
        
        // Callback to refresh the list
        mListView.setOnRefreshListener(new OnRefreshListener() {
          

@Override
public void onRefresh(RefreshableListView listView) {
// TODO Auto-generated method stub
new NewDataTask().execute();
}
        });
    }

    private class NewDataTask extends AsyncTask<Void, Void, String> {

        @Override
        protected String doInBackground(Void... params) {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {}
            
            return "WWW.MUSETHEPLACE.COM";
        }

        @Override
        protected void onPostExecute(String result) {
            mItems.add(0, result);
            // This should be called after refreshing finished
            mListView.completeRefreshing();

            super.onPostExecute(result);
        }
    }

}




RefreshableListView .java


package com.jitesh.android.widget;

import com.jitesh.android.R;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

public class RefreshableListView extends ListView {

    private View mHeaderContainer = null;
    private View mHeaderView = null;
    private ImageView mArrow = null;
    private ProgressBar mProgress = null;
    private TextView mText = null;
    private float mY = 0;
    private float mHistoricalY = 0;
    private int mHistoricalTop = 0;
    private int mInitialHeight = 0;
    private boolean mFlag = false;
    private boolean mArrowUp = false;
    private boolean mIsRefreshing = false;
    private int mHeaderHeight = 0;
    private OnRefreshListener mListener = null;

    private static final int REFRESH = 0;
    private static final int NORMAL = 1;
    private static final int HEADER_HEIGHT_DP = 62;
    private static final String TAG = RefreshableListView.class.getSimpleName();

    public RefreshableListView(final Context context) {
        super(context);
        initialize();
    }

    public RefreshableListView(final Context context, final AttributeSet attrs) {
        super(context, attrs);
        initialize();
    }

    public RefreshableListView(final Context context, final AttributeSet attrs, final int defStyle) {
        super(context, attrs, defStyle);
        initialize();
    }

    public void setOnRefreshListener(final OnRefreshListener l) {
        mListener = l;
    }

    public void completeRefreshing() {
        mProgress.setVisibility(View.INVISIBLE);
        mArrow.setVisibility(View.VISIBLE);
        mHandler.sendMessage(mHandler.obtainMessage(NORMAL, mHeaderHeight, 0));
        mIsRefreshing = false;
        invalidateViews();
    }

    @Override
    public boolean onInterceptTouchEvent(final MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mHandler.removeMessages(REFRESH);
                mHandler.removeMessages(NORMAL);
                mY = mHistoricalY = ev.getY();
                if (mHeaderContainer.getLayoutParams() != null) {
                    mInitialHeight = mHeaderContainer.getLayoutParams().height;
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(final MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_MOVE:
                mHistoricalTop = getChildAt(0).getTop();
                break;
            case MotionEvent.ACTION_UP:
                if (!mIsRefreshing) {
                    if (mArrowUp) {
                        startRefreshing();
                        mHandler.sendMessage(mHandler.obtainMessage(REFRESH, (int) (ev.getY() - mY)
                                / 2 + mInitialHeight, 0));
                    } else {
                        if (getChildAt(0).getTop() == 0) {
                            mHandler.sendMessage(mHandler.obtainMessage(NORMAL,
                                    (int) (ev.getY() - mY) / 2 + mInitialHeight, 0));
                        }
                    }
                } else {
                    mHandler.sendMessage(mHandler.obtainMessage(REFRESH, (int) (ev.getY() - mY) / 2
                            + mInitialHeight, 0));
                }
                mFlag = false;
                break;
        }
        return super.onTouchEvent(ev);
    }

    @Override
    public boolean dispatchTouchEvent(final MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_MOVE && getFirstVisiblePosition() == 0) {
            float direction = ev.getY() - mHistoricalY;
            int height = (int) (ev.getY() - mY) / 2 + mInitialHeight;
            if (height < 0) {
                height = 0;
            }

            float deltaY = Math.abs(mY - ev.getY());
            ViewConfiguration config = ViewConfiguration.get(getContext());
            if (deltaY > config.getScaledTouchSlop()) {

                // Scrolling downward
                if (direction > 0) {
                    // Refresh bar is extended if top pixel of the first item is
                    // visible
                    if (getChildAt(0).getTop() == 0) {
                        if (mHistoricalTop < 0) {

                            // mY = ev.getY(); // TODO works without
                            // this?mHistoricalTop = 0;
                        }

                        // Extends refresh bar
                        setHeaderHeight(height);

                        // Stop list scroll to prevent the list from
                        // overscrolling
                        ev.setAction(MotionEvent.ACTION_CANCEL);
                        mFlag = false;
                    }
                } else if (direction < 0) {
                    // Scrolling upward

                    // Refresh bar is shortened if top pixel of the first item
                    // is
                    // visible
                    if (getChildAt(0).getTop() == 0) {
                        setHeaderHeight(height);

                        // If scroll reaches top of the list, list scroll is
                        // enabled
                        if (getChildAt(1) != null && getChildAt(1).getTop() <= 1 && !mFlag) {
                            ev.setAction(MotionEvent.ACTION_DOWN);
                            mFlag = true;
                        }
                    }
                }
            }

            mHistoricalY = ev.getY();
        }
        try {
            return super.dispatchTouchEvent(ev);
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public boolean performItemClick(final View view, final int position, final long id) {
        if (position == 0) {
            // This is the refresh header element
            return true;
        } else {
            return super.performItemClick(view, position - 1, id);
        }
    }

    private void initialize() {
        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
                Context.LAYOUT_INFLATER_SERVICE);
        mHeaderContainer = inflater.inflate(R.layout.refreshable_list_header, null);
        mHeaderView = mHeaderContainer.findViewById(R.id.refreshable_list_header);
        mArrow = (ImageView) mHeaderContainer.findViewById(R.id.refreshable_list_arrow);
        mProgress = (ProgressBar) mHeaderContainer.findViewById(R.id.refreshable_list_progress);
        mText = (TextView) mHeaderContainer.findViewById(R.id.refreshable_list_text);
        addHeaderView(mHeaderContainer);

        mHeaderHeight = (int) (HEADER_HEIGHT_DP * getContext().getResources().getDisplayMetrics().density);
        setHeaderHeight(0);
    }

    private void setHeaderHeight(final int height) {
        if (height <= 1) {
            mHeaderView.setVisibility(View.GONE);
        } else {
            mHeaderView.setVisibility(View.VISIBLE);
        }

        // Extends refresh bar
        LayoutParams lp = (LayoutParams) mHeaderContainer.getLayoutParams();
        if (lp == null) {
            lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
        }
        lp.height = height;
        mHeaderContainer.setLayoutParams(lp);

        // Refresh bar shows up from bottom to top
        LinearLayout.LayoutParams headerLp = (LinearLayout.LayoutParams) mHeaderView
                .getLayoutParams();
        if (headerLp == null) {
            headerLp = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
                    LayoutParams.WRAP_CONTENT);
        }
        headerLp.topMargin = -mHeaderHeight + height;
        mHeaderView.setLayoutParams(headerLp);

        if (!mIsRefreshing) {
            // If scroll reaches the trigger line, start refreshing
            if (height > mHeaderHeight && !mArrowUp) {
                mArrow.startAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.rotate));
                mText.setText("Release to update");
                rotateArrow();
                mArrowUp = true;
            } else if (height < mHeaderHeight && mArrowUp) {
                mArrow.startAnimation(AnimationUtils.loadAnimation(getContext(), R.anim.rotate));
                mText.setText("Pull down to update");
                rotateArrow();
                mArrowUp = false;
            }
        }
    }

    private void rotateArrow() {
        Drawable drawable = mArrow.getDrawable();
        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
                drawable.getIntrinsicHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        canvas.save();
        canvas.rotate(180.0f, canvas.getWidth() / 2.0f, canvas.getHeight() / 2.0f);
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        drawable.draw(canvas);
        canvas.restore();
        mArrow.setImageBitmap(bitmap);
    }

    private void startRefreshing() {
        mArrow.setVisibility(View.INVISIBLE);
        mProgress.setVisibility(View.VISIBLE);
        mText.setText("Loading...");
        mIsRefreshing = true;

        if (mListener != null) {
            mListener.onRefresh(this);
        }
    }

    private final Handler mHandler = new Handler() {

        @Override
        public void handleMessage(final Message msg) {
            super.handleMessage(msg);

            int limit = 0;
            switch (msg.what) {
                case REFRESH:
                    limit = mHeaderHeight;
                    break;
                case NORMAL:
                    limit = 0;
                    break;
            }

            // Elastic scrolling
            if (msg.arg1 >= limit) {
                setHeaderHeight(msg.arg1);
                int displacement = (msg.arg1 - limit) / 10;
                if (displacement == 0) {
                    mHandler.sendMessage(mHandler.obtainMessage(msg.what, msg.arg1 - 1, 0));
                } else {
                    mHandler.sendMessage(mHandler.obtainMessage(msg.what, msg.arg1 - displacement,
                            0));
                }
            }
        }

    };

    public interface OnRefreshListener {
        public void onRefresh(RefreshableListView listView);
    }

}


refreshable_list_header.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="wrap_content">
    <RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:paddingTop="10dp"
    android:paddingBottom="10dp"
    android:id="@+id/refreshable_list_header">
    <ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="25dp"
    android:src="@drawable/refreshable_listview_arrow"
    android:id="@+id/refreshable_list_arrow" />
    <ProgressBar
    android:layout_width="20dp"
    android:layout_height="20dp"
    android:layout_marginLeft="25dp"
    android:layout_centerVertical="true"
    android:visibility="invisible"
    android:id="@+id/refreshable_list_progress" />
   <TextView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_marginLeft="15dp"
       android:layout_centerInParent="true"
       android:text="Pull down to update"
       android:id="@+id/refreshable_list_text" />
</RelativeLayout>
</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">
<com.jitesh.android.widget.RefreshableListView  
   android:layout_width="fill_parent" 
   android:layout_height="fill_parent"
   android:id="@+id/listview" />
</LinearLayout>


the output is as shown below



Download the code from the link PULL2REFRESH

19 comments:

  1. Want to ask that if i wish to retrieve from the database, how can i do it? and those listed item can be select? to view more details

    ReplyDelete
  2. Hi as you can see the program the loop is adding the data firstlty

    rrayList mItems = new ArrayList();
    for(int i=0;i<10;i++)
    mItems.add("Musethe place "+i);

    here you can retrieve the data from the database and can use the data according to your's need!! when the data is refreshing after the pulling, as you can see the code

    private class NewDataTask extends AsyncTask {

    @Override
    protected String doInBackground(Void... params) {
    try {
    Thread.sleep(3000);
    } catch (InterruptedException e) {}

    return "WWW.MUSETHEPLACE.COM";
    }

    @Override
    protected void onPostExecute(String result) {
    mItems.add(0, result);
    // This should be called after refreshing finished
    mListView.completeRefreshing();

    super.onPostExecute(result);
    }
    } replace the code where you need to invalidate the listview and get the fresh data from the database again for the listview!!

    i think you are firstly getting data from an http connection and storing it into the sqlite database or a file system or else!!and than using it, if i am guessing correctly. so you can proceed in this way. Thanks for the question. hope i gave an idea, if not please get back to me that i can solve the problem, as the same will be useful for me.

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. Thn hw if I selected one of the list, hw it can move to other screen and show the details information?

    ReplyDelete
  5. as i already guessed you are getting data from internet->to-->database ,and than from database for the fresh list , if the list is invalidated, i think it should not be a problem, you can correspondingly get yours data and and can move forward. once in an application i did the pull-2-refresh and was doing things like this and it was not showing a problem while i was moving to next screen, however due to performance hit and some other problems i removed the pull-2-refresh. other wisely one simpler idea is that, once in blackberry i was getting data from database and using it finally from a 2-d array, in this way you can also proceed. please have a look on the logic hope you will be able to rectify it, and also one of my friend is working on the same thing i will try to consult him as well and will reply. Hope for the best for you. you are very new to the blogspot,as you have created in march 2013 (this month only). be in touch.

    ReplyDelete
  6. Mind to gv me the sample that you created? that update from database

    ReplyDelete
  7. Sorry as it is my organizational work at my present company , so really i am sorry that i can not reveal that. anyway the logic is depends on you that how you are developing.

    ReplyDelete
  8. ok, the logic concept i able to understand but just dunno on the way to implement it. anyway thx for explaining on it.

    ReplyDelete
  9. Sorry for not helping you on the full logic side , if you can give me the code than, me and my friends can help you. hope for yours best. great day ahead buddy!! happy coding

    ReplyDelete
  10. lets said that my php(xxxx.com/xxx.php) is getting data like below, hw am i going to implement into this code?

    $q=mysql_query("SELECT .. FROM ... WHERE ID='$_REQUEST[ID]'");

    while($e=mysql_fetch_assoc($q))
    $output[]=$e;

    print(json_encode($output));

    ReplyDelete
  11. Beside that any suggestion that if i want to show few rows of information in the phone where it retrieve from database? such as purchase history

    ReplyDelete
  12. Hi Once you will get the data from the database with the help of the cursor you can set the listview elements from the adapter. it is not a big problem.i am understanding the problem you are facing. i am just giving you the link of the bolg for customlistview . just download the code and use that.
    please have a look.

    http://upadhyayjiteshandroid.blogspot.in/2013/02/android-customlistview-example.html

    ReplyDelete
  13. Thx, I able to do the list view, actually what i want is for customer side i able to get the purchase history and for staff side, can i generate something like graph?

    ReplyDelete
  14. Yes offcourse you can as there are google apis for this purpose you can use them... visit my recent blog about graph api's of google and using them. beside this i can send the demo code of graph engines. give your's mail id.

    ReplyDelete
  15. This comment has been removed by the author.

    ReplyDelete
  16. final CharSequence[] items = {"Foo", "Bar", "Baz"};
    AlertDialog.Builder builder = new AlertDialog.Builder(Main_Menu.this);
    builder.setTitle("Make your selection");
    builder.setItems(items, new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int item) {
    // Do something with the selection
    }
    });
    AlertDialog alert = builder.create();
    alert.show();


    Do you how to make like those listed item is able to link to other screen whn clicked?

    ReplyDelete
  17. yes on the button clicks just call the ntents you want for the action and also in another way you can visit the plog and can make a custom dialogs as well:)
    http://upadhyayjiteshandroid.blogspot.in/2013/03/android-activity-as-dialog.html

    just make buttons and make action listeners to them Thanks.

    ReplyDelete
  18. is ok, I get it in other ways by using on the if and else statement inside. thx

    ReplyDelete
  19. Given so much info in it, These type of articles keeps the users interest in the website, and keep on sharing more .
    Android Training in velachery | Android Training in chennai | Android Training in chennai with placement

    ReplyDelete