水滴状的自己定义视图,让您摆脱单调的Dialog

转载请注明出处:王亟亟的大牛之路

如今各种各样的进度条的呈现方式各种各样,我们老旧的条状条子和转圈圈的方式已经无法满足我们的业务需求,今天亟亟上的是一个水滴状循环滚动的一个自己定义视图。你能够把他用在各种不同的场景下。

先上效果图:

然后又加入了真空,隐藏和显示的效果,如图。

项目结构:

主Activity

public class MainActivity extends AppCompatActivity {

    private MetaballView metaballView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        metaballView = (MetaballView) this.findViewById(R.id.metaball);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_fill) {
            metaballView.setPaintMode(1);
            return true;
        } else if (id == R.id.action_strock) {
            metaballView.setPaintMode(0);
            return true;
        }else if(id==R.id.action_disappear){
            metaballView.setVisibility(View.GONE);
            return  true;
        }else if(id==R.id.action_appear){
            metaballView.setVisibility(View.VISIBLE);
            return  true;
        }

        return super.onOptionsItemSelected(item);
    }
}

CustomView

public class MetaballView extends View {

    private Paint paint = new Paint();
    private float handle_len_rate = 2f;
    private float radius = 30;
    private final int ITEM_COUNT = 6;
    private final int ITEM_DIVIDER = 60;
    private final float SCALE_RATE = 0.3f;
    private float maxLength;
    private ArrayList<Circle> circlePaths = new ArrayList<>();
    private float mInterpolatedTime;
    private MoveAnimation wa;
    private Circle circle;

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

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

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

    }

    private class Circle {
        float[] center;
        float radius;
    }

    public void setPaintMode(int mode) {
        paint.setStyle(mode == 0 ?

Paint.Style.STROKE : Paint.Style.FILL);
    }

    private void init() {
        paint.setColor(Color.WHITE);
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);
        Circle circlePath = new Circle();
        circlePath.center = new float[]{(radius + ITEM_DIVIDER), radius * 1.4f};
        circlePath.radius = radius / 4 * 3;
        circlePaths.add(circlePath);

        for (int i = 1; i < ITEM_COUNT; i++) {
            circlePath = new Circle();
            circlePath.center = new float[]{(radius * 2 + ITEM_DIVIDER) * i, radius * 1.4f};
            circlePath.radius = radius;
            circlePaths.add(circlePath);
        }
        maxLength = (radius * 2 + ITEM_DIVIDER) * ITEM_COUNT;
    }

    private float[] getVector(float radians, float length) {
        float x = (float) (Math.cos(radians) * length);
        float y = (float) (Math.sin(radians) * length);
        return new float[]{
                x, y
        };
    }

    private class MoveAnimation extends Animation {

        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
            mInterpolatedTime = interpolatedTime;
            invalidate();
        }
    }

    /**
     * @param canvas          画布
     * @param j
     * @param i
     * @param v               控制两个圆连接时候长度,间接控制连接线的粗细。该值为1的时候连接线为直线
     * @param handle_len_rate
     * @param maxDistance
     */
    private void metaball(Canvas canvas, int j, int i, float v, float handle_len_rate, float maxDistance) {
        final Circle circle1 = circlePaths.get(i);
        final Circle circle2 = circlePaths.get(j);

        RectF ball1 = new RectF();
        ball1.left = circle1.center[0] - circle1.radius;
        ball1.top = circle1.center[1] - circle1.radius;
        ball1.right = ball1.left + circle1.radius * 2;
        ball1.bottom = ball1.top + circle1.radius * 2;

        RectF ball2 = new RectF();
        ball2.left = circle2.center[0] - circle2.radius;
        ball2.top = circle2.center[1] - circle2.radius;
        ball2.right = ball2.left + circle2.radius * 2;
        ball2.bottom = ball2.top + circle2.radius * 2;

        float[] center1 = new float[]{
                ball1.centerX(),
                ball1.centerY()
        };
        float[] center2 = new float[]{
                ball2.centerX(),
                ball2.centerY()
        };
        float d = getDistance(center1, center2);

        float radius1 = ball1.width() / 2;
        float radius2 = ball2.width() / 2;
        float pi2 = (float) (Math.PI / 2);
        float u1, u2;

        if (d > maxDistance) {
            canvas.drawCircle(ball1.centerX(), ball1.centerY(), circle1.radius, paint);
            canvas.drawCircle(ball2.centerX(), ball2.centerY(), circle2.radius, paint);
        } else {
            float scale2 = 1 + SCALE_RATE * (1 - d / maxDistance);
            float scale1 = 1 - SCALE_RATE * (1 - d / maxDistance);
            radius2 *= scale2;
//            radius1 *= scale1;
            canvas.drawCircle(ball1.centerX(), ball1.centerY(), radius1, paint);
            canvas.drawCircle(ball2.centerX(), ball2.centerY(), radius2, paint);

        }

        Log.d("Metaball_radius", "radius1:" + radius1 + ",radius2:" + radius2);
        if (radius1 == 0 || radius2 == 0) {
            return;
        }

        if (d > maxDistance || d <= Math.abs(radius1 - radius2)) {
            return;
        } else if (d < radius1 + radius2) {
            u1 = (float) Math.acos((radius1 * radius1 + d * d - radius2 * radius2) /
                    (2 * radius1 * d));
            u2 = (float) Math.acos((radius2 * radius2 + d * d - radius1 * radius1) /
                    (2 * radius2 * d));
        } else {
            u1 = 0;
            u2 = 0;
        }
        Log.d("Metaball", "center2:" + Arrays.toString(center2) + ",center1:" + Arrays.toString(center1));
        float[] centermin = new float[]{center2[0] - center1[0], center2[1] - center1[1]};

        float angle1 = (float) Math.atan2(centermin[1], centermin[0]);
        float angle2 = (float) Math.acos((radius1 - radius2) / d);
        float angle1a = angle1 + u1 + (angle2 - u1) * v;
        float angle1b = angle1 - u1 - (angle2 - u1) * v;
        float angle2a = (float) (angle1 + Math.PI - u2 - (Math.PI - u2 - angle2) * v);
        float angle2b = (float) (angle1 - Math.PI + u2 + (Math.PI - u2 - angle2) * v);

        Log.d("Metaball", "angle1:" + angle1 + ",angle2:" + angle2 + ",angle1a:" + angle1a + ",angle1b:" + angle1b + ",angle2a:" + angle2a + ",angle2b:" + angle2b);

        float[] p1a1 = getVector(angle1a, radius1);
        float[] p1b1 = getVector(angle1b, radius1);
        float[] p2a1 = getVector(angle2a, radius2);
        float[] p2b1 = getVector(angle2b, radius2);

        float[] p1a = new float[]{p1a1[0] + center1[0], p1a1[1] + center1[1]};
        float[] p1b = new float[]{p1b1[0] + center1[0], p1b1[1] + center1[1]};
        float[] p2a = new float[]{p2a1[0] + center2[0], p2a1[1] + center2[1]};
        float[] p2b = new float[]{p2b1[0] + center2[0], p2b1[1] + center2[1]};

        Log.d("Metaball", "p1a:" + Arrays.toString(p1a) + ",p1b:" + Arrays.toString(p1b) + ",p2a:" + Arrays.toString(p2a) + ",p2b:" + Arrays.toString(p2b));

        float[] p1_p2 = new float[]{p1a[0] - p2a[0], p1a[1] - p2a[1]};

        float totalRadius = (radius1 + radius2);
        float d2 = Math.min(v * handle_len_rate, getLength(p1_p2) / totalRadius);
        d2 *= Math.min(1, d * 2 / (radius1 + radius2));
        Log.d("Metaball", "d2:" + d2);
        radius1 *= d2;
        radius2 *= d2;

        float[] sp1 = getVector(angle1a - pi2, radius1);
        float[] sp2 = getVector(angle2a + pi2, radius2);
        float[] sp3 = getVector(angle2b - pi2, radius2);
        float[] sp4 = getVector(angle1b + pi2, radius1);
        Log.d("Metaball", "sp1:" + Arrays.toString(sp1) + ",sp2:" + Arrays.toString(sp2) + ",sp3:" + Arrays.toString(sp3) + ",sp4:" + Arrays.toString(sp4));

        Path path1 = new Path();
        path1.moveTo(p1a[0], p1a[1]);
        path1.cubicTo(p1a[0] + sp1[0], p1a[1] + sp1[1], p2a[0] + sp2[0], p2a[1] + sp2[1], p2a[0], p2a[1]);
        path1.lineTo(p2b[0], p2b[1]);
        path1.cubicTo(p2b[0] + sp3[0], p2b[1] + sp3[1], p1b[0] + sp4[0], p1b[1] + sp4[1], p1b[0], p1b[1]);
        path1.lineTo(p1a[0], p1a[1]);
        path1.close();
        canvas.drawPath(path1, paint);

    }

    private float getLength(float[] b) {
        return (float) Math.sqrt(b[0] * b[0] + b[1] * b[1]);
    }

    private float getDistance(float[] b1, float[] b2) {
        float x = b1[0] - b2[0];
        float y = b1[1] - b2[1];
        float d = x * x + y * y;
        return (float) Math.sqrt(d);
    }

    //測试用
//    @Override
//    public boolean onTouchEvent(MotionEvent event) {
//        switch (event.getAction()) {
//            case MotionEvent.ACTION_DOWN:
//                break;
//            case MotionEvent.ACTION_MOVE:
//                Circle circle = circlePaths.get(0);
//                circle.center[0] = event.getX();
//                circle.center[1] = event.getY();
//                invalidate();
//                break;
//            case MotionEvent.ACTION_UP:
//                break;
//        }
//
//        return true;
//    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        circle = circlePaths.get(0);
        circle.center[0] = maxLength * mInterpolatedTime;
        for (int i = 1, l = circlePaths.size(); i < l; i++) {
            metaball(canvas, i, 0, 0.6f, handle_len_rate, radius * 4f);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (ITEM_COUNT * (radius * 2 + ITEM_DIVIDER)), MeasureSpec.EXACTLY);
        heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) (2 * radius * 1.4f), MeasureSpec.EXACTLY);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    private void stopAnimation() {
        this.clearAnimation();
        postInvalidate();
    }

    private void startAnimation() {
        wa = new MoveAnimation();
        wa.setDuration(2500);
        wa.setInterpolator(new AccelerateDecelerateInterpolator());
        wa.setRepeatCount(Animation.INFINITE);
        wa.setRepeatMode(Animation.REVERSE);
        startAnimation(wa);
    }

    @Override
    protected void onVisibilityChanged(View changedView, int visibility) {
        super.onVisibilityChanged(changedView, visibility);

        if (visibility == GONE || visibility == INVISIBLE) {
            stopAnimation();
        } else {
            startAnimation();
        }
    }

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

    @Override
    protected void onDetachedFromWindow() {
        stopAnimation();
        super.onDetachedFromWindow();
    }

}

想改变呈现的方式能够改动Style文件以及setColor这里更改自己想要的颜色。也能够改构造函数用外部传入的方式来活用该类

布局文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#001d30"
    android:clipChildren="false"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <com.dodola.animview.MetaballView
        android:id="@+id/metaball"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true" />
</RelativeLayout>

源代码已上传,地址:http://yunpan.cn/cddIXXQbqTf2m 訪问password 681c

有问题或者有业务QQ452270579交流,谢谢

时间: 2024-08-29 19:08:45

水滴状的自己定义视图,让您摆脱单调的Dialog的相关文章

ANDROID自己定义视图——onLayout源代码 流程 思路具体解释

简单介绍: 在自己定义view的时候.事实上非常easy.仅仅须要知道3步骤: 1.測量--onMeasure():决定View的大小 2.布局--onLayout():决定View在ViewGroup中的位置 3.绘制--onDraw():怎样绘制这个View. 而第3步的onDraw系统已经封装的非常好了,基本不用我们来担心,仅仅须要专注到1,2两个步骤就中好了. 第一步的測量,能够參考我之前的文章:(ANDROID自己定义视图--onMeasure流程.MeasureSpec具体解释) 而

django基础知识之定义视图:

定义视图 本质就是一个函数 视图的参数 一个HttpRequest实例 通过正则表达式组获取的位置参数 通过正则表达式组获得的关键字参数 在应用目录下默认有views.py文件,一般视图都定义在这个文件中 如果处理功能过多,可以将函数定义到不同的py文件中 新建views1.py #coding:utf-8 from django.http import HttpResponse def index(request): return HttpResponse("你好") 在urls.p

Django 定义视图函数

Django 定义视图函数 视图函数主要在Django项目内app下的 views.py 文件内定义 # 调用模块方法 from django.shortcuts import render,HttpResponse,redirect def func() # 包含所有的请求数据 .... # 返回字符串内容 return HttpRespon('字符串') # 返回数据 render(request,'模板路径') 返回html模板内容 return render(request,'index

UI_03 ?定义视图、视图控制器

?.?定义label-textField视图 自定义LTView类,封装UILabel与UITextField,实现快速创建以下类型的视图 **** 使用UIView子类实现 **** LTView.h #import <UIKit/UIKit.h> @interface LTView : UIView @property (nonatomic, retain)UILabel * label; @property (nonatomic, retain)UITextField * textFie

SpringMvc-自定义视图

1.创建视图: 注意:创建视图的时候需要实现View接口的俩个方法 package com.atguigu.springmvc.views; import java.util.Date; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Com

UI-自定义视图、视图控制器

自定义视图 自定义视图:系统标准UI之外,自己组合而出的新的视图. iOS提供了很多UI组件,借助它们,我们可以做各种程序. 创建步骤 创建一个UIView子类 在类的初始化方法中添加子视图 类的.h文件提供一些接口(方法),便于外界操作子视图 视图控制器 UIViewController:视图控制器: 控制视图显示,响应事件: 分担AppDelegate的工作: 实现模块独立,提高复用性. 视图控制器功能 控制试图大小变换.布局视图.响应事件 检测以及处理内存警告 检测以及处理屏幕旋转 检测视

Andriod三步学会安卓自己定义视图及其属性

第一步:自己定义属性 第二步:自己定义控件解析属性 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcm5adW9adW8=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" > 第三步:在XML文件里使用自己定义控件和自己定义属性

Python学习(六) 定义视图以及页面模板

请求解析一般都是通过请求的request获取一定参数,然后根据参数做一定业务逻辑判断,这其中可能包括查询数据库,然后将需要返回的数据封装成一个HttpResponse返回. 代码如下: 这是一个简单的处理请求的函数,对应之前url映射的  url(r'^articles/([0-9]{4})/$', views.year_archive),django会将url中用()包起来的内容作为变量传给函数,此处year_archive中的year变量就是([0-9]{4})代表的值. Article.o

Android自己定义视图(一):带下划线的TextView

package com.francis.underlinetextviewtest; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import an