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


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

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

      无图无真相:

      

      实现了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;
            }
    
        }
    }

    工程源码:点我

  • 相关阅读:
    redis发布订阅
    redis学习笔记(面试题)
    redis安全 (error) NOAUTH Authentication required
    HDU3001 Travelling —— 状压DP(三进制)
    POJ3616 Milking Time —— DP
    POJ3186 Treats for the Cows —— DP
    HDU1074 Doing Homework —— 状压DP
    POJ1661 Help Jimmy —— DP
    HDU1260 Tickets —— DP
    HDU1176 免费馅饼 —— DP
  • 原文地址:https://www.cnblogs.com/bausch/p/2546221.html
Copyright © 2020-2023  润新知