• 安卓弹性刷新通用版—支持任何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;
            }
    
        }
    }

    工程源码:点我

  • 相关阅读:
    Manjaro Linux自带的Python没有安装IDLE的解决办法
    Python入门 | IDLE的介绍和使用方法
    用U盘装CentOS 7出现dracut:/#问题的解决办法
    在Ubuntu下,如何安装坚果云deb文件
    windows7下进行ubuntu U盘启动盘的制作
    oracle 死锁和锁等待区别
    MySQL数据库设计总结
    oracle开机自启
    微信备份提示当前网络状况复杂,请尝试使用其他网络的解决方法
    ORA-27090 Unable to reserve kernel resources for asynchronous disk I/O
  • 原文地址:https://www.cnblogs.com/bausch/p/2546221.html
Copyright © 2020-2023  润新知