自定义ViewFlipper 实现滚动效果

自定义view实现了类似百度手机助手,首页评论滚动效果。

看效果:


gif做的不好,其效果就是:几个viewitem不停的向上滚动,新加入item有个淡入的效果。


说下实现思路:自定义view继承至LinearLayout,控制item数量及其动画效果,实现item复用,传入数据即可,使用方便。

代码:

/**
 * Jiantao.Yang
 *
 * @description 仿百度手机助手,评论滚动效果
 * @time 2015/1/16 17:37
 */
public class ViewFlipper extends LinearLayout {

    private final int MAX_SHOW_ITEM_SIZE = 5;

    private IAdapter mIAdapter;

    private int mCount;

    //最后一个item动画
    private Animation mLastOneAnimation;

    //其它item动画
    private Animation mCommonAnimation;

    //数据下标
    private int mCurrentIndex;

    /**
     * 这里动画时间是1600毫秒,所以间隔得大于动画时间
     */
    private static final int DEFAULT_INTERVAL = 2000;

    private int mFlipInterval = DEFAULT_INTERVAL;

    private boolean mAutoStart = false;

    private boolean mRunning = false;
    private boolean mStarted = false;
    private boolean mVisible = false;
    private boolean mUserPresent = true;

    public ViewFlipper(Context context) {
        super(context);
        init(context);
    }

    public ViewFlipper(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public ViewFlipper(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                mUserPresent = false;
                updateRunning();
            } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
                mUserPresent = true;
                updateRunning(false);
            }
        }
    };

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        // Listen for broadcasts related to user-presence
        final IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_USER_PRESENT);

        // OK, this is gross but needed. This class is supported by the
        // remote views machanism and as a part of that the remote views
        // can be inflated by a context for another user without the app
        // having interact users permission - just for loading resources.
        // For exmaple, when adding widgets from a user profile to the
        // home screen. Therefore, we register the receiver as the current
        // user not the one the context is for.
        getContext().registerReceiver(mReceiver, filter);

        if (mAutoStart) {
            // Automatically start when requested
            startFlipping();
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        mVisible = false;

        getContext().unregisterReceiver(mReceiver);
        updateRunning();
    }

    @Override
    protected void onWindowVisibilityChanged(int visibility) {
        super.onWindowVisibilityChanged(visibility);
        mVisible = visibility == VISIBLE;
        updateRunning(mVisible);
//        updateRunning(false);
    }

    private void init(Context context) {
        this.setOrientation(LinearLayout.VERTICAL);
    }

    public void setIAdapter(IAdapter iAdapter) {
        this.mIAdapter = iAdapter;
        initShowItems();
    }

    public void startFlipping() {
        mStarted = true;
        updateRunning();
    }

    public void stopFlipping() {
        mStarted = false;
        updateRunning();
    }

    private void updateRunning() {
        updateRunning(true);
    }

    /**
     * Returns true if the child views are flipping.
     */
    public boolean isFlipping() {
        return mStarted;
    }

    /**
     * Set if this view automatically calls {@link #startFlipping()} when it
     * becomes attached to a window.
     */
    public void setAutoStart(boolean autoStart) {
        mAutoStart = autoStart;
    }

    /**
     * Returns true if this view automatically calls {@link #startFlipping()}
     * when it becomes attached to a window.
     */
    public boolean isAutoStart() {
        return mAutoStart;
    }

    @Override
    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
        super.onInitializeAccessibilityEvent(event);
        event.setClassName(ViewFlipper.class.getName());
    }

    @Override
    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
        super.onInitializeAccessibilityNodeInfo(info);
        info.setClassName(ViewFlipper.class.getName());
    }

    /**
     * 初始化childViews
     */
    private void initShowItems() {
        if (mIAdapter != null) {
            mCount = mIAdapter.getCount();
            for (int i = 0; i < mCount; i++) {
                if (i == MAX_SHOW_ITEM_SIZE) {
                    break;
                }
                View convertView = getChildAt(i);
                View item = mIAdapter.getItemView(convertView, i);
                addView(item, i);
            }
        }
    }

    /**
     * Internal method to start or stop dispatching flip {@link android.os.Message} based
     * on {@link #mRunning} and {@link #mVisible} state.
     *
     * @param flipNow Determines whether or not to execute the animation now, in
     *                addition to queuing future flips. If omitted, defaults to
     *                true.
     */
    private void updateRunning(boolean flipNow) {
        boolean running = mVisible && mStarted && mUserPresent;
        System.out.println(" updateRunning running:" + running + " mVisible " + mVisible + " userPresent " + mUserPresent);
        if (running != mRunning) {
            if (running && (mCount > MAX_SHOW_ITEM_SIZE)) {
                showItems(mCurrentIndex++, flipNow);
                Message msg = mHandler.obtainMessage(FLIP_MSG);
                mHandler.sendMessageDelayed(msg, mFlipInterval);
            } else {
                mHandler.removeMessages(FLIP_MSG);
            }
            mRunning = running;
        }
    }


    private void showItems(final int position, boolean animate) {
        if (animate && (mLastOneAnimation == null || mCommonAnimation == null)) {
            mLastOneAnimation = AnimationUtils.loadAnimation(getContext(), R.anim.lastone_anim);
            mCommonAnimation = AnimationUtils.loadAnimation(getContext(), R.anim.common_anim);
        }
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            child.clearAnimation();
            int index = position + i;
            child = mIAdapter.getItemView(child, (index >= mIAdapter.getCount()) ? (index - mIAdapter.getCount()) : index);
            if (animate) {
                if (i == childCount - 1) {
                    child.setAnimation(mLastOneAnimation);
                } else {
                    child.setAnimation(mCommonAnimation);
                }
            }
            child.setVisibility(View.VISIBLE);
        }
        if (animate) {
            mCommonAnimation.startNow();
            mLastOneAnimation.startNow();
        }

        //保证传入的position小于getCount
        if (mCurrentIndex >= mIAdapter.getCount()) {
            mCurrentIndex = 0;
        }
    }

    private final int FLIP_MSG = 1;

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == FLIP_MSG) {
                if (mRunning) {
                    showItems(mCurrentIndex++, true);
                    msg = obtainMessage(FLIP_MSG);
                    sendMessageDelayed(msg, mFlipInterval);
                }
            }
        }
    };

    public interface IAdapter {

        /**
         * @param convertView
         * @param position
         * @return
         */
        public View getItemView(View convertView, int position);

        /**
         * @return 数据count
         */
        public int getCount();

    }

}

再来看看调用部分:

public class MainActivity extends ActionBarActivity implements ViewFlipper.IAdapter {

    ViewFlipper viewFlipper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        viewFlipper = (ViewFlipper) findViewById(R.id.view_flipper);
        viewFlipper.setIAdapter(this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        viewFlipper.startFlipping();
    }

    @Override
    public View getItemView(View convertView, int position) {
        View item = null;
        TextView textView;
        if (convertView == null) {
            item = View.inflate(this, R.layout.item, null);
        } else {
            item = convertView;
        }
        textView = (TextView) item.findViewById(R.id.textview);
        textView.setText("测试数据:" + position);
        return item;
    }

    @Override
    public int getCount() {
        return 8;
    }
}

可以看出,MainActivity实现了ViewFlipper.IAdapter接口,setAdapter后调用startFlipper即可。


这里布局文件我就不贴出来了,附上工程源码,项目里动画时间有点长,修改下就ok。


限于水平有限,不足之处难免,望各位不舍指正,与君共勉。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值