安卓弹性刷新通用版—支持任何view上下刷新均有弹性效果

  做安卓开发的朋友们,不知道你们会不会经常用遇到这样的情景,某个做所畏的产品设计的sb,拿着iphone来看给你看,说看苹果的这个效果多好,看那个效果多好。苹果也比安卓清晰多了,你能不能也把咱们的安卓应用也做成这种效果的。那样的用户体验更好,更cool一些。不知你们有没有遇到过这样半吊子的sb设计,反正我是遇到了。所以本文由此而生。

  进入正题:首先你要实现弹性效果的view要能确定什么时候应该出现下拉的效果,什么时候出现下推的效果。在代码里的体现就是你要实现IScrollOverable接口。本文中的例子就拿GridView来做个例子。

  无图无真相:

  

安卓弹性刷新通用版—支持任何view上下刷新均有弹性效果

 

安卓弹性刷新通用版—支持任何view上下刷新均有弹性效果

  实现了IScrollOverable接口的GridView:

  

 1 public class BshSOGridView extends GridView implements

 2         IScrollOverable

 3 {

 4     public BshSOGridView(Context context, AttributeSet attrs,

 5             int defStyle)

 6     {

 7         super( context, attrs, defStyle );

 8     }

 9 

10     public BshSOGridView(Context context, AttributeSet attrs)

11     {

12         super( context, attrs );

13     }

14 

15     public BshSOGridView(Context context)

16     {

17         super( context );

18     }

19 

20     @Override

21     public boolean isScrollOnTop()

22     {

23         return 0 == getFirstVisiblePosition() ? true : false;

24     }

25 

26     @Override

27     public boolean isScrollOnBtm()

28     {

29         return (getCount() - 1) == getLastVisiblePosition() ? true : false;

30     }

31 }

  

  处理弹性效果的view.实际上是把想要有弹性效果的view加到这个view里来。

/**

 * 本类旨在实现通用的弹性刷新效果。只要实现了IScrollOverable的view都可以据有弹性效果。上弹下弹均可以哦!

 * 

 * @author http://www.cnblogs.com/bausch/

 * 

 */

public class BshElasticView extends LinearLayout

{

    final int RELEASE_H = 0;

    final int PULL = 1;

    final int REFRESHING_H = 2;

    final int PUSH = 3;

    final int RELEASE_F = 4;

    final int REFRESHING_F = 5;

    final int NORMAL = 6;

    int factor = 3;

    float maxElastic = 0.2f;

    int critical = 2;

    int maxElasticH, maxElasticF;

    boolean isTopRecored;

    boolean isBtmRecored = false;

    int startY;

    IRefresh irefresh;

    RotateAnimation animation;

    RotateAnimation reverseAnimation;

    int state = NORMAL;

    boolean isBack;



    View rv;

    RelativeLayout headerView, footerView, continer, main_continer;

    ProgressBar hPro, fPro;

    ImageView hArow, fArow;

    TextView tvTipH, tvTipF, tvLstH, tvLstF;

    Context ctx;

    private IScrollOverable overable;

    public int continerH, continerW, headerH, headerW, footerH, footerW, rvH,

            rvW;

    Handler handler = new Handler()

    {



        @Override

        public void handleMessage(Message msg)

        {

            switch ( msg.what )

            {

            case 0:

                Log.d( "bsh", "刷新完成,到normal状态" );

                state = NORMAL;

                changeFooterViewByState();

                break;

            }

        }



    };



    public BshElasticView(Context context, AttributeSet attrs, int defStyle)

    {

        super( context, attrs );

        init( context );

    }



    public BshElasticView(Context context, AttributeSet attrs)

    {

        super( context, attrs );

        init( context );

    }



    public BshElasticView(Context context)

    {

        super( context );

        init( context );

    }



    public void setScrollOverable(IScrollOverable o)

    {

        overable = o;

        addView( ( View ) o );

    }



    public void init(Context ctx)

    {

        this.ctx = ctx;

        rv = View.inflate( ctx, R.layout.elastic_view, null );

        main_continer = ( RelativeLayout ) rv.findViewById( R.id.main_continer );



        headerView = ( RelativeLayout ) rv.findViewById( R.id.header );

        hPro = ( ProgressBar ) rv.findViewById( R.id.pro_h );

        hArow = ( ImageView ) rv.findViewById( R.id.iv_harow );

        tvTipH = ( TextView ) rv.findViewById( R.id.tv_htip );

        tvLstH = ( TextView ) rv.findViewById( R.id.tv_lst );

        measureViewHeight( headerView );

        headerH = headerView.getMeasuredHeight();

        headerW = headerView.getMeasuredWidth();

        footerView = ( RelativeLayout ) rv.findViewById( R.id.footer );

        fPro = ( ProgressBar ) rv.findViewById( R.id.pro_f );

        fArow = ( ImageView ) rv.findViewById( R.id.iv_farow );

        tvTipF = ( TextView ) rv.findViewById( R.id.tv_ftip );

        tvLstF = ( TextView ) rv.findViewById( R.id.tv_lstf );

        footerH = headerH;

        footerW = headerW;



        continer = ( RelativeLayout ) rv.findViewById( R.id.continer );

        addView( rv, new ViewGroup.LayoutParams( LayoutParams.FILL_PARENT,

                LayoutParams.FILL_PARENT ) );

        main_continer.setPadding( 0, -1 * headerH, 0, 0 );

        rv.invalidate();

        rv.requestLayout();



        animation = new RotateAnimation( 0, -180,

                RotateAnimation.RELATIVE_TO_SELF, 0.5f,

                RotateAnimation.RELATIVE_TO_SELF, 0.5f );

        animation.setDuration( 500 );

        animation.setFillAfter( true );



        reverseAnimation = new RotateAnimation( -180, 0,

                RotateAnimation.RELATIVE_TO_SELF, 0.5f,

                RotateAnimation.RELATIVE_TO_SELF, 0.5f );

        reverseAnimation.setDuration( 500 );

        reverseAnimation.setFillAfter( true );



        state = NORMAL;

    }



    private void measureViewHeight(View child)

    {

        ViewGroup.LayoutParams p = child.getLayoutParams();



        if ( null == p )

        {

            p = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT,

                    ViewGroup.LayoutParams.FILL_PARENT );

        }



        int childHeightSpec = ViewGroup

                .getChildMeasureSpec( 0, 0 + 0, p.height );

        int lpWidth = p.width;

        int childWidthSpec;

        if ( lpWidth > 0 )

        {

            childWidthSpec = MeasureSpec.makeMeasureSpec( lpWidth,

                    MeasureSpec.EXACTLY );

        }

        else

        {

            childWidthSpec = MeasureSpec.makeMeasureSpec( 0,

                    MeasureSpec.UNSPECIFIED );

        }

        child.measure( childWidthSpec, childHeightSpec );

    }



    @Override

    protected void onLayout(boolean changed, int l, int t, int r, int b)

    {

        super.onLayout( changed, l, t, r, b );

        rvH = b;

        rvW = r;

        footerH = headerH;

        footerW = headerW;

        continerH = rvH;

        continerW = rvW;

        maxElasticH = ( int ) (headerH + continerH * maxElastic);

        maxElasticF = 0 - maxElasticH;

        RelativeLayout.LayoutParams rl = new RelativeLayout.LayoutParams( rvW,

                rvH );

        rl.addRule( RelativeLayout.BELOW, R.id.header );

        continer.setLayoutParams( rl );

        continer.requestLayout();

    }



    public interface IScrollOverable

    {

        public boolean isScrollOnTop();



        public boolean isScrollOnBtm();

    }



    public void addView(View v)

    {

        continer.addView( v, new RelativeLayout.LayoutParams(

                RelativeLayout.LayoutParams.FILL_PARENT,

                RelativeLayout.LayoutParams.FILL_PARENT ) );

    }



    public void changeHeaderViewByState()

    {

        switch ( state )

        {

        case RELEASE_H:

            hArow.setVisibility( View.VISIBLE );

            hPro.setVisibility( View.GONE );

            tvTipH.setVisibility( View.VISIBLE );

            tvLstH.setVisibility( View.VISIBLE );

            hArow.clearAnimation();

            hArow.startAnimation( animation );

            tvTipH.setText( "松开刷新" );

            Log.d( "bsh", "当前状态,松开刷新" );

            break;

        case PULL:

            hPro.setVisibility( View.GONE );

            tvTipH.setVisibility( View.VISIBLE );

            tvLstH.setVisibility( View.VISIBLE );

            hArow.clearAnimation();

            hArow.setVisibility( View.VISIBLE );

            if ( isBack )

            {

                isBack = false;

                hArow.clearAnimation();

                hArow.startAnimation( reverseAnimation );

            }

            tvTipH.setText( "下拉刷新" );

            Log.d( "bsh", "当前状态,上推刷新" );

            break;

        case REFRESHING_H:

            hPro.setVisibility( View.VISIBLE );

            hArow.clearAnimation();

            hArow.setVisibility( View.GONE );

            tvTipH.setText( "正在刷新..." );

            tvLstH.setVisibility( View.INVISIBLE );

            tvLstH.setText( "上次更新:"

                    + Calendar.getInstance().getTime().toLocaleString() );

            Log.d( "bsh", "当前状态,正在刷新..." );

            break;

        case NORMAL:

            setRvPadding( -1 * headerH );

            hPro.setVisibility( View.GONE );

            hArow.clearAnimation();

            hArow.setImageResource( R.drawable.arrow_down );

            tvTipH.setText( "下拉刷新" );

            tvLstH.setVisibility( View.VISIBLE );

            Log.d( "bsh", "当前状态,normalf" );

            break;

        }

    }



    public void changeFooterViewByState()

    {

        switch ( state )

        {

        case RELEASE_F:

            fArow.setVisibility( View.VISIBLE );

            fPro.setVisibility( View.GONE );

            tvTipF.setVisibility( View.VISIBLE );

            tvLstF.setVisibility( View.VISIBLE );

            fArow.clearAnimation();

            fArow.startAnimation( animation );

            tvTipF.setText( "松开刷新" );

            Log.d( "bsh", "当前状态,松开刷新" );

            break;

        case PUSH:

            fPro.setVisibility( View.GONE );

            tvTipF.setVisibility( View.VISIBLE );

            tvLstF.setVisibility( View.VISIBLE );

            fArow.clearAnimation();

            fArow.setVisibility( View.VISIBLE );

            if ( isBack )

            {

                isBack = false;

                fArow.clearAnimation();

                fArow.startAnimation( reverseAnimation );

            }

            tvTipF.setText( "上推刷新" );

            Log.d( "bsh", "当前状态,上推刷新" );

            break;

        case REFRESHING_F:

            fPro.setVisibility( View.VISIBLE );

            fArow.clearAnimation();

            fArow.setVisibility( View.GONE );

            tvTipF.setText( "正在刷新..." );

            tvLstF.setVisibility( View.INVISIBLE );

            tvLstF.setText( "上次更新:"

                    + Calendar.getInstance().getTime().toLocaleString() );

            Log.d( "bsh", "当前状态,正在刷新..." );

            break;

        case NORMAL:

            setRvPadding( -1 * headerH );

            fPro.setVisibility( View.GONE );

            fArow.clearAnimation();

            fArow.setImageResource( R.drawable.arrow_up );

            tvTipF.setText( "上推刷新" );

            tvLstF.setVisibility( View.VISIBLE );

            Log.d( "bsh", "当前状态,normalf" );

            break;

        }

    }



    public void setRvPadding(int padding)

    {

        Log.d( "bsh", "padding:" + padding );

        if ( padding < maxElasticF )

        {

            return;

        }

        else if ( padding > maxElasticH )

        {

            return;

        }



        main_continer.setPadding( 0, padding, 0, 0 );

        rv.requestLayout();

    }



    @Override

    public boolean dispatchTouchEvent(MotionEvent event)

    {

        switch ( event.getAction() )

        {

        case MotionEvent.ACTION_DOWN:

            startRecord( event );

            break;

        case MotionEvent.ACTION_UP:

            handleUpF();

            handleUpH();

            break;

        case MotionEvent.ACTION_MOVE:

            handleMove( event );

            break;

        }

        if ( state == NORMAL || state == REFRESHING_H || state == REFRESHING_F )

        {

            return super.dispatchTouchEvent( event );

        }

        else

            return true;

    }



    public void handleMove(MotionEvent event)

    {

        int tempY = ( int ) event.getY();

        tempY = tempY < 0 ? 0 : tempY;

        Log.d( "bsh", "get temp:" + tempY );

        startRecord( event );

        if ( state != REFRESHING_F && isBtmRecored )

        {

            handleMoveF( tempY );

        }

        else if ( state != REFRESHING_H && isTopRecored )

        {

            handleMoveH( tempY );

        }

    }



    public void handleMoveH(int tempY)

    {

        Log.d( "bsh", "release to refresh h" );

        if ( state == RELEASE_H )

        {

            Log.d( "bsh", "release to refresh h" );

            if ( ((tempY - startY) / factor < headerH * critical)

                    && (tempY - startY) > 0 )

            {

                state = PULL;

                changeHeaderViewByState();

                Log.d( "bsh", "由松开刷新状态转变到下拉刷新状态" );

            }

            else if ( startY - tempY > 0 )

            {

                state = NORMAL;

                changeHeaderViewByState();

                Log.d( "bsh", "由松开刷新状态转变到normal状态" );

            }

        }

        if ( state == PULL )

        {

            Log.d( "bsh", "push to refresh" );

            if ( (tempY - startY) / factor >= headerH * critical )

            {

                state = RELEASE_H;

                isBack = true;

                changeHeaderViewByState();

                Log.d( "bsh", "由normal或者下拉刷新状态转变到松开刷新" );

            }

            else if ( tempY - startY <= 0 )

            {

                state = NORMAL;

                changeHeaderViewByState();

                Log.d( "bsh", "由normal或者下拉刷新状态转变到normal状态" );

            }

        }



        if ( state == NORMAL )

        {

            if ( tempY - startY > 0 )

            {

                Log.d( "bsh", "normalf to push to refersh" );

                state = PULL;

                changeHeaderViewByState();

            }

        }



        // 这里就是处理弹性效果的地方

        if ( state == PULL || state == RELEASE_H )

        {

            setRvPadding( (tempY - startY) / factor - headerH );

        }

    }



    public void handleMoveF(int tempY)

    {

        if ( state == RELEASE_F )

        {

            Log.d( "bsh", "release to refresh f" );

            if ( ((tempY - startY) / factor > 0 - headerH * critical)

                    && (tempY - startY) < 0 )

            {

                state = PUSH;

                changeFooterViewByState();

                Log.d( "bsh", "由松开刷新状态转变到上推刷新状态" );

            }

            else if ( tempY - startY >= 0 )

            {

                state = NORMAL;

                changeFooterViewByState();

                Log.d( "bsh", "由松开刷新状态转变到normal状态" );

            }

        }

        if ( state == PUSH )

        {

            Log.d( "bsh", "push to refresh" );

            if ( (tempY - startY) / factor <= (0 - headerH * critical) )

            {

                state = RELEASE_F;

                isBack = true;

                changeFooterViewByState();

                Log.d( "bsh", "由normal或者上推刷新状态转变到松开刷新" );

            }

            else if ( tempY - startY >= 0 )

            {

                state = NORMAL;

                changeFooterViewByState();

                Log.d( "bsh", "由normal或者上推刷新状态转变到normal状态" );

            }

        }



        if ( state == NORMAL )

        {

            if ( tempY - startY < 0 )

            {

                Log.d( "bsh", "normalf to push to refersh" );

                state = PUSH;

                changeFooterViewByState();

            }

        }



        // 这里就是处理弹性效果的地方。

        if ( state == PUSH || state == RELEASE_F )

        {

            setRvPadding( (tempY - startY) / factor - headerH * 2 );

        }

    }



    public void startRecord(MotionEvent event)

    {

        if ( overable.isScrollOnTop() && !isTopRecored )

        {

            isTopRecored = true;

            startY = ( int ) event.getY();

        }

        if ( overable.isScrollOnBtm() && !isBtmRecored )

        {

            isBtmRecored = true;

            startY = ( int ) event.getY();

        }

    }



    public void handleUpF()

    {

        if ( state != REFRESHING_F )

        {

            if ( state == PUSH )

            {

                state = NORMAL;

                changeFooterViewByState();

                Log.d( "bsh", "由上推刷新状态,到normal状态" );

            }



            if ( state == RELEASE_F )

            {

                state = REFRESHING_F;

                changeFooterViewByState();

                setRvPadding( -1 * headerH - 1 );

                irefresh.refreshBtm();

            }

        }

        isBtmRecored = false;

        isBack = false;

    }



    public void handleUpH()

    {

        if ( state != REFRESHING_H )

        {

            if ( state == PULL )

            {

                state = NORMAL;

                changeHeaderViewByState();

                Log.d( "bsh", "由下拉状态,到normal状态" );

            }



            if ( state == RELEASE_H )

            {

                state = REFRESHING_H;

                changeHeaderViewByState();

                setRvPadding( 0 );

                irefresh.refreshTop();

            }

        }

        isTopRecored = false;

        isBack = false;

    }



    public void onRefreshComplete()

    {

        handler.sendEmptyMessage( 0 );

    }



    public interface IRefresh

    {

        public boolean refreshTop();



        public boolean refreshBtm();

    }



    /***

     * 设置拉动效果幅度建议值(1-5)

     * 

     * @param factor

     */

    public void setFactor(int factor)

    {

        this.factor = factor;

    }



    /***

     * 设置最大拉动的位置,请在(0.0f-1.0f)之间取值

     * 

     * @param maxElastic

     */

    public void setMaxElastic(float maxElastic)

    {

        this.maxElastic = maxElastic;

    }



    /***

     * 设置拉动和松开状态切换的临界值,1-5之间

     * 

     * @param critical

     */

    public void setCritical(int critical)

    {

        this.critical = critical;

    }



}

  调用Activity:

public class BshSOViewActivity extends Activity {

    BshElasticView ev;

    BshSOGridView gv;

    GridAdagper ga = new GridAdagper();

    

    @Override

    public void onCreate(Bundle savedInstanceState)

    {

        super.onCreate( savedInstanceState );

        setContentView( R.layout.elastic_grid );

        ev = ( BshElasticView ) findViewById( R.id.ev );

        //拉动幅度

        ev.setFactor( 2 );

        //拉动范围

        ev.setMaxElastic( 0.9f );

        gv = new BshSOGridView( this );

        gv.setBackgroundColor( Color.WHITE );

        gv.setNumColumns( 4 );

        gv.setAdapter( ga );

        ev.setScrollOverable( gv );

        ev.irefresh = new IRefresh()

        {

            @Override

            public boolean refreshTop()

            {

                new Thread( new Runnable()

                {

                    @Override

                    public void run()

                    {

                        try

                        {

                            Log.d( "bsh", "refreshing" );

                            //在这里做刷新操作读数据神马的。这里用睡觉代替

                            Thread.sleep( 3000 );



                        } catch ( InterruptedException e )

                        {

                            e.printStackTrace();

                        }

                        ev.onRefreshComplete();



                    }

                } ).start();

                return false;

            }



            @Override

            public boolean refreshBtm()

            {

                new Thread( new Runnable()

                {

                    @Override

                    public void run()

                    {

                        try

                        {

                            Log.d( "bsh", "refreshing" );

                            Thread.sleep( 3000 );

                        } catch ( InterruptedException e )

                        {

                            e.printStackTrace();

                        }

                        ev.onRefreshComplete();

                    }

                } ).start();

                return false;

            }

        };

    }



    class GridAdagper extends BaseAdapter

    {



        @Override

        public int getCount()

        {

            return 100;

        }



        @Override

        public Object getItem(int arg0)

        {

            return null;

        }



        @Override

        public long getItemId(int arg0)

        {

            return 0;

        }



        @Override

        public View getView(int arg0, View arg1, ViewGroup arg2)

        {

            if ( null == arg1 )

            {

                arg1 = new ImageView( BshSOViewActivity.this );

                arg1.setBackgroundResource( R.drawable.ic_launcher );

            }

            return arg1;

        }



    }

}

 

工程源码:点我

你可能感兴趣的:(view)