徐徐展开的画卷

写了个简单的自定义控件,能把View像打开画卷一样徐徐展开的ViewGroup,山河画卷.放在这个ViewGroup里面的View可以被这个ViewGroup控件徐徐打开,然后图穷匕首现

效果如下

“1”是可以更换的图片,最初想法是一个向右的箭头.

继承FrameLayout,因为FrameLayout耗资源少,而且onLayout什么的都写好了

//像山河画卷一样展开的View
public class PictureScrollView extends FrameLayout {public PictureScrollView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init();}public PictureScrollView(Context context, AttributeSet attrs) {super(context, attrs);init();}public PictureScrollView(Context context) {super(context);init();}

先把子控件画出来,右侧不让显示的地方我们用画笔擦除调,需要申明一个擦除画笔,和一个画卷的引导卷轴,即效果图中的”1”

    //清除画笔private Paint mClearPaint;// Reel卷轴,指示当前展开到的位置private Bitmap reel;// 控制卷轴的宽高private float mReelWidth, mReelHeight;//卷轴绘制到画板的区域private RectF dstReel = new RectF();//卷轴图片源,即reel的矩形区域private Rect srcReel;private void init() {mClearPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mClearPaint.setColor(Color.BLUE);//画笔的混合模式,为清除像素mClearPaint.setXfermode(new PorterDuffXfermode(Mode.CLEAR));reel = BitmapFactory.decodeResource(getResources(), R.drawable.icon_select);srcReel = new Rect(0, 0, reel.getWidth(), reel.getHeight());Log.d("px", "srcReel-->" + srcReel.toString());}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);mWidth = getMeasuredWidth();mHeight = getMeasuredHeight();mClearRect.set(mProgress, 0, mWidth, getMeasuredHeight());// 设置卷轴的宽高if (mReelWidth == 0) {// 按图片的实际宽高// mReelWidth = reel.getWidth();// mReelHeight = reel.getHeight();// 或者应该指定按控件的百分比,而不是完全按卷轴图片的宽高来mReelHeight = mHeight / 10;mReelWidth = reel.getWidth() * mReelHeight / reel.getHeight();}//设置卷轴绘画到的区域,应位于当前展开进度的正中心dstReel.set(mProgress - mReelWidth / 2  , mHeight / 2 - mReelHeight / 2, mReelWidth / 2 + mProgress, mHeight / 2+ mReelHeight / 2);Log.d("px", "dstReel-->" + dstReel.toString());}

mProgress指目前画卷展开的进度,就是x轴的距离.mClearRect等声明在下面

绘制过程,super.dispatchDraw是绘制子View..在用擦除画笔时必须先保存为图层,否则将擦除一切.

    private RectF rect = new RectF(), mClearRect = new RectF();@Overrideprotected void dispatchDraw(Canvas canvas) {rect.set(canvas.getClipBounds());// canvas.drawColor(0x00ffffff);int saveCount = canvas.saveLayer(rect, null, Canvas.ALL_SAVE_FLAG);//这里null在xml预览报错,要不报错传个全局new paint吧.super.dispatchDraw(canvas);canvas.drawRect(mClearRect, mClearPaint);canvas.restoreToCount(saveCount);if (showReel) {canvas.drawBitmap(reel, srcReel, dstReel, null);}}

余下代码

    // 启动画卷模式private boolean startDrawProgress = false;// 从0到完全展开的时间private long completelyOpenTime = 2000;//是否显示引导卷轴private boolean showReel = true;public void setProgress(float progress) {this.mProgress = progress;mClearRect.set(progress, 0, mWidth, mHeight);invalidate();}@Overridepublic boolean dispatchTouchEvent(MotionEvent e) {switch (e.getAction() & MotionEvent.ACTION_MASK) {case MotionEvent.ACTION_DOWN:Log.d("px", "ACTION_DOWN");// 点到卷轴,则启动画卷模式if (startDrawProgress = checkEventAtReel(e)) {mProgress = e.getX();mClearRect.set(mProgress, 0, mWidth, mHeight);dstReel.left = mProgress - mReelWidth / 2;dstReel.right = mProgress + mReelWidth / 2;if (observer != null) {observer.onScroll(mProgress);}invalidate();}// 点击到进度外看不见的地方,拒绝该事件,也不向下传递事件else if (e.getX() > mProgress) {return false;}break;case MotionEvent.ACTION_MOVE:Log.d("px", "ACTION_MOVE");if (startDrawProgress) {mProgress = e.getX();mClearRect.set(mProgress, 0, mWidth, mHeight);dstReel.left = mProgress - mReelWidth / 2;dstReel.right = mProgress + mReelWidth / 2;if (observer != null) {observer.onScroll(mProgress);}invalidate();}break;case MotionEvent.ACTION_CANCEL:case MotionEvent.ACTION_UP:if (startDrawProgress) {if (mProgress > mWidth / 2) {// 大于一半自动全部展开openFromCurrentPosition();} else {// 小于一半自动全部收回closeFromCurrentPosition();}}break;default:break;}super.dispatchTouchEvent(e);return true;}private ValueAnimator animator;private ValueAnimator.AnimatorUpdateListener updateListener = new AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animator) {float v = (Float) animator.getAnimatedValue();mClearRect.set(mProgress = v, 0, mWidth, getMeasuredHeight());dstReel.left = mProgress - mReelWidth / 2;dstReel.right = mProgress + mReelWidth / 2;if (observer != null) {observer.onScroll(mProgress);}invalidate();}};// 大于一半自动全部展开private void openFromCurrentPosition() {if (animator != null) {animator.cancel();}animator = ValueAnimator.ofFloat(mProgress, mWidth);animator.addUpdateListener(updateListener);animator.addListener(new SimpleAnimatorListener() {@Overridepublic void onAnimationEnd(Animator animation) {if (observer != null) {observer.onOpen();}}});animator.setDuration((long) ((mWidth - mProgress) / mWidth * completelyOpenTime));animator.setInterpolator(new DecelerateInterpolator());animator.start();}// 小于一半自动全部收回private void closeFromCurrentPosition() {if (animator != null) {animator.cancel();}animator = ValueAnimator.ofFloat(mProgress, 0);animator.addUpdateListener(updateListener);animator.addListener(new SimpleAnimatorListener() {@Overridepublic void onAnimationEnd(Animator animation) {if (observer != null) {observer.onClose();}}});animator.setDuration((long) (mProgress / mWidth * completelyOpenTime));animator.setInterpolator(new DecelerateInterpolator());animator.start();}public boolean isShowReel() {return showReel;}public void setShowReel(boolean showReel) {this.showReel = showReel;}/*** 检查事件是否在可点击的卷轴上* * @param e* @return*/private boolean checkEventAtReel(MotionEvent e) {// 卷轴不可见,则非if (!showReel) {return false;}if (e.getX() < dstReel.left) {return false;}if (e.getX() > dstReel.right) {return false;}if (e.getY() < dstReel.top) {return false;}if (e.getY() > dstReel.bottom) {return false;}return true;}/*** 观察者,监控展开与收缩的过程* @author user**/public interface Observer {// 完全展开void onOpen();// 完全关闭void onClose();// 滚动中void onScroll(float dx);}// 观察者,监听view的展开和收拢过程private Observer observer;public Observer getObserver() {return observer;}public void setObserver(Observer observer) {this.observer = observer;}

观察者模式用来允许使用时拓展,比如

        final PictureScrollView picture = (PictureScrollView) findViewById(R.id.pictureScrollView1);picture.setObserver(new Observer() {@Overridepublic void onScroll(float dx) {//随着进度慢慢淡入pictureScrollView.setAlpha(dx/pictureScrollView.getWidth());}@Overridepublic void onOpen() {//完全展开后不允许再卷回去了pictureScrollView.setShowReel(false);}@Overridepublic void onClose() {}});

实现点击卷轴自动展开,从右向左展开,竖向展开等等,可以修改一些代码做到

不是所有效果都需要自定义View,有时候一个Action也可以完成

百叶窗

下面是一个百叶窗效果的实现,利用View的绕x=a轴旋转变换

大致效果图是这样,但GIF没有我的手机上表现流畅

    private ListView docScanListView;private boolean isBlindAniming = false;private Handler handler = new Handler();/*** * @描述:百叶窗效果* * @作者 [pWX273343] 2015年11月6日*/private void doBlind() {docScanListView.setVisibility(View.VISIBLE);docScanListView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {@SuppressLint("NewApi")@Overridepublic void onGlobalLayout() {docScanListView.getViewTreeObserver().removeOnGlobalLayoutListener(this);if (isBlindAniming) {return;} else {isBlindAniming = true;}final int count = docScanListView.getChildCount();// 设置初始状态,-90从开始出现for (int i = 0; i < count; i++) {final View view;view = docScanListView.getChildAt(i);view.setPivotX(view.getWidth() / 2f);view.setPivotY(view.getHeight() / 2f);view.setRotationX(90);}// 对每个item项做翻转动画for (int i = 0; i < count; i++) {final View view;view = docScanListView.getChildAt(count - i - 1);final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);animator.setDuration(250);animator.setInterpolator(new DecelerateInterpolator());animator.addUpdateListener(new AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float v = (Float) animation.getAnimatedValue();view.setScaleX(0.8f + 0.2f * v);view.setScaleY(0.8f + 0.2f * v);view.setRotationX(90 - 90 * v);}});// 最后一个View动画添加结束监听if (i == count - 1) {animator.addListener(new SimpleAnimatorListener() {public void onAnimationEnd(Animator animation) {isBlindAniming = false;}});}handler.postDelayed(new Runnable() {@Overridepublic void run() {animator.start();}}, 100 * i);}}});}/*** * @描述:百叶窗效果退出* * @param needDismiss*            是否需要关闭弹窗* @作者 [pWX273343] 2015年11月11日*/public void dismissBlind() {if (isBlindAniming) {return;} else {isBlindAniming = true;}if (docScanListView.getVisibility() != View.VISIBLE) {isBlindAniming = false;return;}final int count = docScanListView.getChildCount();for (int i = 0; i < count; i++) {final View view = docScanListView.getChildAt(i);view.setPivotX(view.getWidth() / 2f);view.setPivotY(view.getHeight() / 2f);final ValueAnimator animator = ValueAnimator.ofFloat(1, 0);animator.setDuration(250);animator.setInterpolator(new AccelerateInterpolator());animator.addUpdateListener(new AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float v = (Float) animation.getAnimatedValue();view.setScaleX(0.8f + 0.2f * v);view.setScaleY(0.8f + 0.2f * v);view.setRotationX(90 - 90 * v);}});// 最后一个做完添加关闭弹窗if (i == count - 1)animator.addListener(new SimpleAnimatorListener() {@Overridepublic void onAnimationEnd(Animator animation) {isBlindAniming = false;docScanListView.setVisibility(View.GONE);}});handler.postDelayed(new Runnable() {@Overridepublic void run() {animator.start();}}, 100 * i);}}

android自定义控件江河画卷,以及ListView百叶窗效果.相关推荐

  1. android 语音搜索动画,Android自定义控件实现UC浏览器语音搜索效果

    最近项目上要实现语音搜索功能,界面样式要模仿一下UC浏览器的样式,UC浏览器中有一个控件,会随着声音大小浮动,然后寻思偷个懒,百度一下,结果也没有找到类似的,只能自己动手了. 先上图看我实现的效果: ...

  2. android组件的下拉回弹,Android自定义控件仿ios下拉回弹效果

    网上有很多类似的文章,大多数还是继承listview来实现(主要是listview.addHeaderView()和listview.addFooterView在listview的首尾添加view,也 ...

  3. Android自定义控件之3D上下翻页效果的倒计时控件

    这是一个自定义的倒计时控件,具有3D上下翻页翻转效果.最近项目中需要做一个倒计时控件,需要和iOS端的效果保持一样.大致效果是这样的,如下图所示: 由于暂时还不会怎么样制作gif动态图,所以想看具体效 ...

  4. Android 高仿 QQ5.0 侧滑菜单效果 自定义控件来袭

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39257409,本文出自[张鸿洋的博客] 上一篇博客带大家实现了:Android ...

  5. Android实现ListView圆角效果

    本文演示如何Android中实现ListView圆角效果. 无论是网站,还是APP,人们都爱看一些新颖的视图效果.直角看多了,就想看看圆角,这几年刮起了一阵阵的圆角设计风:CSS新标准纳入圆角元素,特 ...

  6. Android 实现ListView圆角效果

     今天,简单讲讲如何实现使用  ListView显示圆角. 其实代码很多都可以解决,这是在网上搜索的一个解决的代码. 无论是网站,还是APP,人们都爱看一些新颖的视图效果.直角看多了,就想看看 ...

  7. Android魔法(第四弹)—— 一步步实现百叶窗效果

    目录 1.效果展示 2.实现AnimationViewInterface接口 3.解析动画组成 4.翻转单元--RotateView 1)前景背景图 2)实现翻转 3)实现翻转动画 5.百叶窗--Bl ...

  8. 自定义控件android特效,Android自定义控件eBook实现翻书效果实例详解

    本文实例讲述了Android自定义控件eBook实现翻书效果的方法.分享给大家供大家参考,具体如下: 效果图: Book.java文件: package com.book; import androi ...

  9. Android小項目之---ListView实现论坛管理效果(附源碼)

    在android系統中,ListView的用法稍微複雜一點,配置Adpater就有幾種方法,如ArrayAdapter,SimpleAdapter等.查了一些網上的相關例子,有很多都是用ListAct ...

最新文章

  1. 汇编语言中带点/小数点的是什么
  2. 浅析网站页面设计需要注意哪些细节问题?
  3. 从面向过程到面向对象
  4. memcpy实例(一)
  5. csdn开发者报告中学习到的新知识
  6. mysql分页查询关键_MySQL优化教程之超大分页查询
  7. mysql判断是否为null_MySQL如何判断字段是否为null
  8. c语言int的存储形式,C语言变量的存储类型有3种,即int型、float型和char型
  9. Ecstore中的微信支付怎么样配置
  10. .net mvc html5,带有.NET MVC 3 Razor Editor的HTML5占位符
  11. 路由协议之间的经典对比
  12. 自定义填充图案插件 cad_CAD填充技巧:填充图案
  13. tomcat乱码怎么解决
  14. matlab 直方图 拟合,MATLAB 直方图拟合
  15. 创业一年半项目经验分享
  16. html设置背景图片颜色,CSS设置背景图片及背景颜色示例
  17. 网络游戏开发实战-坦克大战学习问题记录
  18. 【3分钟速读】那些你苦苦搜索的模板,是这么被捣腾出来的
  19. 小学生html教程,小学.html
  20. 小虎电商浏览器:拼多多层级有什么作用?多多参谋客服为你解答

热门文章

  1. 系统编程02-管道PIPE(pipe、mkfifo、access)
  2. 百度之星2017资格赛T3 度度熊与邪恶大魔王 背包
  3. 山西高企认定申报连连失败?从这五个方面找原因【企策通】
  4. 我的世界java版的武器伤害_我的世界各种伤害数据计算
  5. 快速克隆网站(Teleport Ultra)
  6. 游戏经济学,第 3 部分:免费游戏
  7. 拼多多、饿了么、蚂蚁金服Java面试题大集
  8. 自由Windows软件
  9. 【嵌入式软件开发实习】个人面试记录及其总结(一)
  10. 使用NPOI实现在Excel第一行插入文字