Android 自定义实现翻转卡片的View

一般一个View只有一面,但是可以自定义一个View,实现像翻书那样的翻转效果。

旋转View:

/**
 * 两种方式构造一个翻转卡片
 * 1:直接提供一个特定命名格式的View
 * 2:提供两个线性布局(正面和,反面)
 * Created by lip on 2015/4/8.
 */
public class FlipView extends LinearLayout implements View.OnClickListener,RotateAnimation.InterpolatedTimeListener
{
    private LinearLayout m_first_ll, m_second_ll;

    private boolean enableRefresh;
    private LinearLayout view;
    private View clickView;//当前的view
    private Context context;
    public FlipView(Context context)
    {
        super(context);
        this.context=context;
        //initViews();
    }
    public FlipView(Context context,AttributeSet attrs)
    {
        super(context,attrs);
        this.context=context;
        //initViews();
    }

    /**
     */
    public void initViews()
    {
        view=(LinearLayout)inflate(context,R.layout.flip_view,null);
        m_first_ll=(LinearLayout)view.findViewById(R.id.first_ll);
        m_second_ll=(LinearLayout)view.findViewById(R.id.second_ll);
        m_first_ll.setOnClickListener(this);
        m_second_ll.setOnClickListener(this);
        addView(view, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    }

    /**
     * @param ll1 正面
     * @param ll2  反面
     */
    public void addViews(LinearLayout ll1,LinearLayout ll2)
    {
        m_first_ll=ll1;
        m_second_ll=ll2;
        m_first_ll.setOnClickListener(this);
        m_second_ll.setOnClickListener(this);
        addView(m_first_ll, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        addView(m_second_ll, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    }

    /**
     * flag=0 翻到正面
     * flag=1 翻到反面
     * @param flag
     */
    public void show(int flag)
    {
        enableRefresh = true;
        RotateAnimation rotateAnim = null;
        float cX = this.getWidth() / 2.0f;
        float cY = this.getHeight() / 2.0f;
        if(flag==0)
        rotateAnim = new RotateAnimation(cX, cY,
                RotateAnimation.ROTATE_DECREASE);
        else if(flag==1)
            rotateAnim = new RotateAnimation(cX, cY,
                    RotateAnimation.ROTATE_INCREASE);
        if (rotateAnim != null) {
            rotateAnim.setInterpolatedTimeListener(this);
            rotateAnim.setFillAfter(true);
            this.startAnimation(rotateAnim);
        }
    }
    @Override
    public void onClick(View v) {
        Log.d("click:",v.toString());
        enableRefresh = true;
        clickView=v;
        RotateAnimation rotateAnim = null;
        float cX = this.getWidth() / 2.0f;
        float cY = this.getHeight() / 2.0f;
        if (m_first_ll==v) {
            rotateAnim = new RotateAnimation(cX, cY,
                    RotateAnimation.ROTATE_INCREASE);
        } else if (m_second_ll == v) {
            rotateAnim = new RotateAnimation(cX, cY,
                    RotateAnimation.ROTATE_DECREASE);
        }

        if (rotateAnim != null) {
            rotateAnim.setInterpolatedTimeListener(this);
            rotateAnim.setFillAfter(true);
            this.startAnimation(rotateAnim);
        }
    }

    @Override
    public void interpolatedTime(float interpolatedTime) {
        if (enableRefresh && interpolatedTime > 0.5f) {
            setHint();
            enableRefresh = false;
        }
    }

    public void setHint() {
        if (clickView == m_first_ll) {
            m_first_ll.setVisibility(View.GONE);
            m_second_ll.setVisibility(View.VISIBLE);
        } else if (clickView==m_second_ll) {
            m_second_ll.setVisibility(View.GONE);
            m_first_ll.setVisibility(View.VISIBLE);
        }

    }
}

来看看使用方法:

public class FlipActivity extends Activity
{
    private FlipView flipView;
    LinearLayout firstLL,secondLL;
    LinearLayout root;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_flip);
        initViews();
    }
    private void initViews()
    {
        root=(LinearLayout)LayoutInflater.from(this).inflate(R.layout.activity_flip,null);
        flipView=(FlipView)root.findViewById(R.id.flip_view);
        /*********第一种方式(要主动调用initViews)*************/
//        firstLL=(LinearLayout)LayoutInflater.from(this).inflate(R.layout.flip_view1,null);
//        secondLL=(LinearLayout)LayoutInflater.from(this).inflate(R.layout.flip_view2,null);
        /*********第二种方式*************/
        firstLL=(LinearLayout)root.findViewById(R.id.root_ll1);
        secondLL=(LinearLayout)root.findViewById(R.id.root_ll2);
        root.removeView(firstLL);
        root.removeView(secondLL);
        flipView.addViews(firstLL,secondLL);
        setContentView(root);
    }
}

既然一个View 有两面,那当然需要主动去设置正面和反面的内容了。

   flipView.addViews(firstLL,secondLL);第一个参数就是正面的view,第二个参数是反面的view,这两个view都是线性布局。我提供了两种设置正反面的的方式,如果要是对于布局有一点了解,其实这是一样的
</pre><p>    旋转工具类(网上参考别人的):</p><p>          <pre name="code" class="java">public class RotateAnimation extends Animation {

	/** 值为true时可明确查看动画的旋转方向。 */
	public static final boolean DEBUG = false;
	/** 沿Y轴正方向看,数值减1时动画逆时针旋转。 */
	public static final boolean ROTATE_DECREASE = true;
	/** 沿Y轴正方向看,数值减1时动画顺时针旋转。 */
	public static final boolean ROTATE_INCREASE = false;
	/** Z轴上最大深度。 */
	public static final float DEPTH_Z = 310.0f;
	/** 动画显示时长。 */
	public static final long DURATION = 800l;
	/** 图片翻转类型。 */
	private final boolean type;
	private final float centerX;
	private final float centerY;
	private Camera camera;

	public RotateAnimation(float cX, float cY, boolean type) {
		centerX = cX;
		centerY = cY;
		this.type = type;
		// 设置动画时长
		setDuration(DURATION);
	}

	@Override
	public void initialize(int width, int height, int parentWidth,
			int parentHeight) {
		// 在构造函数之后、applyTransformation()之前调用本方法。
		super.initialize(width, height, parentWidth, parentHeight);
		camera = new Camera();
	}

	@Override
	protected void applyTransformation(float interpolatedTime,
			Transformation transformation) {
		// interpolatedTime:动画进度值,范围为0~1,0.5为正好翻转一半
		if (listener != null) {
			listener.interpolatedTime(interpolatedTime);
		}

		float from = 0.0f, to = 0.0f;
		if (type == ROTATE_DECREASE) {
			from = 0.0f;
			to = 180.0f;
		} else if (type == ROTATE_INCREASE) {
			from = 360.0f;
			to = 180.0f;
		}

		// 旋转的角度
		float degree = from + (to - from) * interpolatedTime;
		boolean overHalf = (interpolatedTime > 0.5f);
		if (overHalf) {
			// 翻转过半的情况下,为保证数字仍为可读的文字而非镜面效果的文字,需翻转180度。
			degree = degree - 180;
		}

		// 旋转深度
		float depth = (0.5f - Math.abs(interpolatedTime - 0.5f)) * DEPTH_Z;

		final Matrix matrix = transformation.getMatrix();
		camera.save();
		// 深度——》相当于与屏幕的距离
		camera.translate(0.0f, 0.0f, depth);
		// 以x轴旋转
		// camera.rotateX(degree);
		// 以y轴旋转
		camera.rotateY(degree);
		camera.getMatrix(matrix);
		camera.restore();

		if (DEBUG) {
			if (overHalf) {
				matrix.preTranslate(-centerX * 2, -centerY);
				matrix.postTranslate(centerX * 2, centerY);
			}
		} else {
			// 确保图片的翻转过程一直处于组件的中心点位置
			/*
			 * preTranslate是指在setScale前平移,postTranslate是指在setScale后平移,它们参数是平移的距离,
			 * 而不是平移目的地的坐标!
			 * 由于缩放是以(0,0)为中心的,所以为了把界面的中心与(0,0)对齐,就要preTranslate(-centerX,
			 * -centerY),setScale完成后, 调用postTranslate(centerX,
			 * centerY),再把图片移回来,这样看到的动画效果就是activity的界面图片从中心不停的缩放了
			 * 注:centerX和centerY是界面中心的坐标
			 */
			matrix.preTranslate(-centerX, -centerY);
			matrix.postTranslate(centerX, centerY);
		}
	}

	/** 用于监听动画进度。当值过半时需更新的内容。 */
	private InterpolatedTimeListener listener;

	public void setInterpolatedTimeListener(InterpolatedTimeListener listener) {
		this.listener = listener;
	}

	/** 动画进度监听器。 */
	public static interface InterpolatedTimeListener {
		public void interpolatedTime(float interpolatedTime);
	}

}

xml布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:orientation="vertical"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
    <cn.xindrace.viewflip.FlipView
        android:id="@+id/flip_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    <LinearLayout
        android:id="@+id/root_ll1"
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="It is the first side"/>

    </LinearLayout>
    <LinearLayout
        android:id="@+id/root_ll2"
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="It is the first side"/>

    </LinearLayout>

</LinearLayout>
时间: 2024-10-05 04:11:34

Android 自定义实现翻转卡片的View的相关文章

Android自定义带边框的圆形view

由于项目需要,需要做一个圆形的带边框并且里边还有文字的view →_→ ↓↓↓↓这样↓↓↓↓ 如果在布局文件中做的话是非常麻烦的,而且复用性也不高.所以想到用自定义一个view的来实现该功能,这样封装性和复用性就会相对提高,可方便在以后类似的项目中使用.可能也有同学有过这样的需求,所以在这分享出来供大家参考,不足之处还请多多指点. 看代码: 1package com.stock.manage.friend.view;import android.content.Context; 2 import

Android 自定义录音、播放动画View,让你的录音浪起来

最近公司项目有一个录音的录制和播放动画需求,然后时间是那么紧,那么赶紧开撸. 先看效果图 嗯,然后大致就是这样,按住录音,然后有一个倒计时,最外层一个进度条,还有一个类似模拟声波的动画效果(其实中间的波浪会根据声音的大小浪起来的~) 2 实现思路 然后,我们适当的来分析一下这个录音动画的实现方式.这个肯定是通过自定义控件,咱们来把这个效果完完全全画出来. 大致包括以下几个点: 1. 最外层的进度条,最坑的就是一开始的一个渐变的效果 2. 然后进度条最前方是有一个点的(我肯定选择用图片来实现) 3

[原] Android 自定义View步骤

例子如下:Android 自定义View 密码框 例子 1 良好的自定义View 易用,标准,开放. 一个设计良好的自定义view和其他设计良好的类很像.封装了某个具有易用性接口的功能组合,这些功能能够有效地使用CPU和内存,并且十分开放的.但是,除了开始一个设计良好的类之外,一个自定义view应该: l 符合安卓标准 l 提供能够在Android XML布局中工作的自定义样式属性 l 发送可访问的事件 l 与多个Android平台兼容. Android框架提供了一套基本的类和XML标签来帮您创

Android 自定义View合集

自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/Mr-XiaoLiang 自定义控件三部曲 http://blog.csdn.net/harvic880925?viewmode=contents Android 从0开始自定义控件之View基础知识与概念 http://blog.csdn.net/airsaid/article/details/5

Android自定义View探索(一)—生命周期

Activity代码: public class FiveActivity extends AppCompatActivity { private MyView myView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.e("log", "Activity生命周期:onCreate"); setConte

Android 自定义View视图

创建全新的视图将满足我们独特的UI需求. 本文介绍在指南针开发中会用到的罗盘的界面UI,通过继承View类实现的自定义视图,以此来深刻了解自定义视图. 实现效果图: 源代码: 布局文件activity_main(其中CompassView继承View类): <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.

Android开发之自定义View-可动画展开收缩View的实现

有时候需要点击一个view可以动画展开和收缩折叠一个View这样的效果,这样就可以直接自定义View来实现. 本例中,采用继承FrameLayout来实现自定义的ExpandView.下面将详细介绍各个部分来实现该类以及如何使用该自定义视图. 效果图如下: 未展开效果: 正在向上折叠收缩中的效果: 已经展开效果: 自定义展开类:ExpandView的实现: package com.czm.customview; import android.content.Context; import and

android自定义View (一)MeasureSpec

A MeasureSpec encapsulates the layout requirements passed from parent to child. Each MeasureSpec represents a requirement for either the width or the height. A MeasureSpec is comprised of a size and a mode. There are three possible modes: UNSPECIFIED

Android自定义View之LoadingLayout

介绍 上一篇博文写了一个通用的加载view,这篇在加载view的基础在包裹一层就是LoadingLayout了,主要的目的是免去每次加载时要隐藏主内容布局,然后加载成功之后显示主内容布局这些繁琐操作.如果你还不了解loadingView,可以简单的看一下上一篇博文:Android 自定义通用的loadingview,实现原理很简单,就是LoadingLayout在包裹内容层的基础上,在代码里添加loadingView作为第二个子view,所以不做过多讲解,大家看完直接下载源码参考. Loadin