定制视图,项目名:PersonDraw
效果就是能手动在屏幕上绘制图形。
1.创建的视图类DesignView.java继承自View类。
public class DesignView extends View {
private static final String TAG="DesignView";
private Box mBox;
private List<Box> mBoxlist=new ArrayList<>();
private Paint mBoxPaint;
private Paint MBackgroundPaint;
public DesignView(Context context){
this(context,null);
}
public DesignView(Context context, AttributeSet attrs){
super(context,attrs);
mBoxPaint=new Paint();
mBoxPaint.setColor(0x22ff0000);
MBackgroundPaint=new Paint();
MBackgroundPaint.setColor(0xfff8efe0);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
PointF current=new PointF(event.getX(),event.getY());
String action="";
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
action="ACTION_DOWN";
mBox=new Box(current);
mBoxlist.add(mBox);
break;
case MotionEvent.ACTION_MOVE:
action="ACTION_MOVE";
if(mBox!=null){
mBox.setMxian(current);
invalidate();
}
break;
case MotionEvent.ACTION_UP:
action="ACTION_UP";
mBox=null;
break;
case MotionEvent.ACTION_CANCEL:
action="ACTION_CANCEL";
mBox=null;
break;
}
Log.i(TAG,action+current.x+current.y);
return true;
}
@Override
protected void onDraw(Canvas canvas) {//真正实现绘图的方法
canvas.drawPaint(MBackgroundPaint);
for (Box box:mBoxlist){
float left = Math.min(box.getMyuan().x, box.getMxian().x);
float right = Math.max(box.getMyuan().x, box.getMxian().x);
float top = Math.min(box.getMyuan().y, box.getMxian().y);
float bottom = Math.max(box.getMyuan().y, box.getMxian().y);
canvas.drawRect(left,top,right,bottom,mBoxPaint);
}
}
}
两个构造方法的目的是让视图可从代码或者布局文件进行实例化。
首先是重写onTouchEvent()方法来记录四个动作,这里使用了PointF类记录屏幕的x和y坐标。还有这里创建了一个Box类是用来表示我定义的矩形框两个x,y坐标的。只要Action_down发生就以当前坐标新建一个Box类,并且存进数组中,当手指移动就能获取第二个坐标进行矩形的绘制。调用invalidate()方法是为了强制重新绘制,并再次调用onDraw()方法,这样才能实时地看到我们绘制的矩形框。
public class Box {
private PointF myuan;
private PointF mxian;
public PointF getMyuan() {
return myuan;
}
public void setMyuan(PointF myuan) {
this.myuan = myuan;
}
public PointF getMxian() {
return mxian;
}
public void setMxian(PointF mxian) {
this.mxian = mxian;
}
public Box(PointF ori) {
myuan=ori;
mxian=ori;
}
}
而在XML文件当中可以这样使用:
<phtotoshow.com.example.persondraw.DesignView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
这里再说说这里用到的Canvas类和Paint类这安卓系统的两大绘制类:
Canvas:所有绘制操作,在那里绘制以及绘制什么。
Paint:绘制图形的特征,什么字体,线条颜色。
再这个Designview中,在构造方法初始化Paint类,并设置好颜色。
然后就是在Draw()方法中填充canvas。并完成相应的坐标计算,最后调用 canvas.drawRect()方法绘制。
属性动画
实现的是点击屏幕日落日出的动画。
首先为了显示出一个圆,在drawable文件下添加上一个xml文件,
内容是:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/bright_sun"/>
</shape>
下面这个是整个场景的布局:
这样就能看到具体的布局。下面就是在具体的Fragment中显示出来,在activity中托管fragment:
public class SunsetActivity extends SingleFragmentActivity {
@Override
protected Fragment createFragment() {
return SunsetFragment.newInstance();
}
}
public class SunsetFragment extends Fragment {
private View mSceneView;
private View mSunView;
private View mSkyView;
private int mBlueSkyColor;
private int mSunsetSkyColor;
private int mNightSkyColor;
private int flag=0;
public static SunsetFragment newInstance() {
return new SunsetFragment();
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View v=inflater.inflate(R.layout.fragment_sunset,container,false);
mSceneView=v;
mSunView=v.findViewById(R.id.sun);
mSkyView=v.findViewById(R.id.sky);
Resources resources = getResources();//获取颜色
mBlueSkyColor = resources.getColor(R.color.blue_sky);
mSunsetSkyColor = resources.getColor(R.color.sunset_sky);
mNightSkyColor = resources.getColor(R.color.night_sky);
mSceneView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (flag) {
case 0:
startAnimation();
flag=1;
break;
case 1:
startAnimation1();
flag=0;
break;
}
}
});
return v;
}
private void startAnimation(){
float start=mSunView.getTop();
float end=mSkyView.getHeight();
//第一个动画
ObjectAnimator heightAnimator=ObjectAnimator.ofFloat(mSunView,"y",start,end)
.setDuration(3000);
heightAnimator.setInterpolator(new AccelerateDecelerateInterpolator());//改变速度
//第二个动画
ObjectAnimator skyAnimator=ObjectAnimator.ofInt(mSkyView,"backgroundColor",mBlueSkyColor,mSunsetSkyColor)
.setDuration(3000);
skyAnimator.setEvaluator(new ArgbEvaluator());//让颜色变化变得自然
heightAnimator.start();//动画开始
//第三个动画
ObjectAnimator nightSkyAnimator = ObjectAnimator
.ofInt(mSkyView, "backgroundColor", mSunsetSkyColor, mNightSkyColor)
.setDuration(1500);
nightSkyAnimator.setEvaluator(new ArgbEvaluator());
//动画集
AnimatorSet animatorSet = new AnimatorSet();
animatorSet
.play(heightAnimator)
.with(skyAnimator)
.before(nightSkyAnimator);
animatorSet.start();
}
private void startAnimation1(){
float sunYStart = mSunView.getTop();
float sunYEnd = mSkyView.getHeight();
flag=0;
ObjectAnimator nightSkyAnimator = ObjectAnimator
.ofInt(mSkyView, "backgroundColor", mNightSkyColor,mSunsetSkyColor )
.setDuration(1500);
nightSkyAnimator.setEvaluator(new ArgbEvaluator());
ObjectAnimator heightAnimator = ObjectAnimator
.ofFloat(mSunView, "y", sunYEnd, sunYStart)
.setDuration(3000);
heightAnimator.setInterpolator(new AccelerateInterpolator());
ObjectAnimator sunsetSkyAnimator = ObjectAnimator
.ofInt(mSkyView, "backgroundColor", mSunsetSkyColor, mBlueSkyColor)
.setDuration(3000);
sunsetSkyAnimator.setEvaluator(new ArgbEvaluator());
AnimatorSet animatorSet = new AnimatorSet();
animatorSet
.play(heightAnimator)
.with(nightSkyAnimator)
.before(sunsetSkyAnimator);
animatorSet.start();
}
}
其余的不多说,主要是定义了3个View类,分别太阳,天空以及海面。
通过调用startAnimation()方法获取视图的顶部坐标位置,从而为下面对太阳下落的距离进行计算。下一步是创建模拟太阳的ObiectAnimator对象,这个就是属性动画制作对象,该对象的setInterpolator方法可实现不同的动画特效,这里是太阳慢慢下落再加速坠落。能发现这里一共有两个ObiectAnimator对象,第二个是实现天空色彩的变换。而其中
skyAnimator.setEvaluator(new ArgbEvaluator());//让颜色变化变得自然,用于计算颜色变化的递进值。
最后就是使用动画集AnimatorSet类来进行多个动画的播放,同样是调用strart()方法来启动。