嵌入式动画,画圆并旋转图像

时间:2018-12-05 10:08:50

标签: android android-animation

我正在努力制作像钢笔画圆一样的动画。为此,我尝试使用“定制”进度栏,并尝试了不同的定制搜索栏库,但是对于带有定制搜索栏的库,我无法设置大拇指。 形象是我想要的方向。它将从顶部开始,并绕一个整圈。 我也使圆圈显示,但是我无法添加和同步图像以同时旋转 enter image description here

2 个答案:

答案 0 :(得分:3)

如果您不想使用图书馆,请尝试

创建这样的类

  

CircularSeekBar

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class CircularSeekBar extends View {

    private Context mContext;
    private OnSeekChangeListener mListener;
    private Paint circleColor;
    private Paint innerColor;
    private Paint circleRing;
    private int angle = 0;
    private int startAngle = 270;
    private int barWidth = 5;
    private int width;
    private int height;
    private int maxProgress = 100;
    private int progress;
    private int progressPercent;
    private float innerRadius;
    private float outerRadius;
    private float cx;
    private float cy;
    private float left;
    private float right;
    private float top;
    private float bottom;
    private float dx;
    private float dy;
    private float startPointX;
    private float startPointY;
    private float markPointX;
    private float markPointY;
    private float adjustmentFactor = 100;
    private Bitmap progressMark;
    private Bitmap progressMarkPressed;
    private boolean IS_PRESSED = false;
    private boolean CALLED_FROM_ANGLE = false;
    private boolean SHOW_SEEKBAR = true;

    private RectF rect = new RectF();

    {
        mListener = new OnSeekChangeListener() {

            @Override
            public void onProgressChange(CircularSeekBar view, int newProgress) {

            }
        };

        circleColor = new Paint();
        innerColor = new Paint();
        circleRing = new Paint();

        circleColor.setColor(Color.parseColor("#ff33b5e5")); // Set default

        innerColor.setColor(Color.WHITE); // Set default background color to black

        circleRing.setColor(Color.WHITE);// Set default background color to Gray

        circleColor.setAntiAlias(true);
        innerColor.setAntiAlias(true);
        circleRing.setAntiAlias(true);

        circleColor.setStrokeWidth(5);
        innerColor.setStrokeWidth(5);
        circleRing.setStrokeWidth(5);

        circleColor.setStyle(Paint.Style.FILL);
    }

    public CircularSeekBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mContext = context;
        initDrawable();
    }

    public CircularSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        initDrawable();
    }

    public CircularSeekBar(Context context) {
        super(context);
        mContext = context;
        initDrawable();
    }

    public void initDrawable() {

        progressMark = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.ic_fav_round);

        progressMarkPressed = BitmapFactory.decodeResource(mContext.getResources(),
                R.mipmap.ic_fav_round);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        width = getWidth(); // Get View Width
        height = getHeight();// Get View Height

        int size = (width > height) ? height : width; // Choose the smaller

        cx = width / 2; // Center X for circle
        cy = height / 2; // Center Y for circle
        outerRadius = (float) ((size*0.60) / 2); // Radius of the outer circle

        innerRadius = outerRadius - barWidth; // Radius of the inner circle

        left = cx - outerRadius; // Calculate left bound of our rect
        right = cx + outerRadius;// Calculate right bound of our rect
        top = cy - outerRadius;// Calculate top bound of our rect
        bottom = cy + outerRadius;// Calculate bottom bound of our rect

        startPointX = cx; // 12 O'clock X coordinate
        startPointY = cy - outerRadius;// 12 O'clock Y coordinate
        markPointX = startPointX;// Initial locatino of the marker X coordinate
        markPointY = startPointY;// Initial locatino of the marker Y coordinate

        rect.set(left, top, right, bottom); // assign size to rect
    }

    @Override
    protected void onDraw(Canvas canvas) {

        canvas.drawCircle(cx, cy, outerRadius, circleRing);
        canvas.drawArc(rect, startAngle, angle, true, circleColor);
        canvas.drawCircle(cx, cy, innerRadius, innerColor);
        if (SHOW_SEEKBAR) {
            dx = getXFromAngle();
            dy = getYFromAngle();
            drawMarkerAtProgress(canvas);
        }
        super.onDraw(canvas);
    }

    public void drawMarkerAtProgress(Canvas canvas) {
        if (IS_PRESSED) {
            canvas.drawBitmap(progressMarkPressed, dx, dy, null);
        } else {
            canvas.drawBitmap(progressMark, dx, dy, null);
        }
    }

    public float getXFromAngle() {
        int size1 = progressMark.getWidth();
        int size2 = progressMarkPressed.getWidth();
        int adjust = (size1 > size2) ? size1 : size2;
        float x = markPointX - (adjust / 2);
        return x;
    }

    public float getYFromAngle() {
        int size1 = progressMark.getHeight();
        int size2 = progressMarkPressed.getHeight();
        int adjust = (size1 > size2) ? size1 : size2;
        float y = markPointY - (adjust / 2);
        return y;
    }

    public int getAngle() {
        return angle;
    }

    public void setAngle(int angle) {
        this.angle = angle;
        float donePercent = (((float) this.angle) / 360) * 100;
        float progress = (donePercent / 100) * getMaxProgress();
        setProgressPercent(Math.round(donePercent));
        CALLED_FROM_ANGLE = true;
        setProgress(Math.round(progress));
    }

    public void setSeekBarChangeListener(OnSeekChangeListener listener) {
        mListener = listener;
    }

    public OnSeekChangeListener getSeekBarChangeListener() {
        return mListener;
    }

    public int getBarWidth() {
        return barWidth;
    }

    public void setBarWidth(int barWidth) {
        this.barWidth = barWidth;
    }

    public interface OnSeekChangeListener {


        public void onProgressChange(CircularSeekBar view, int newProgress);
    }

    public int getMaxProgress() {
        return maxProgress;
    }

    public void setBitmapThumbIcon(int id) {

        progressMark = BitmapFactory.decodeResource(mContext.getResources(), id);

        progressMarkPressed = BitmapFactory.decodeResource(mContext.getResources(),
                id);
    }

    public void setMaxProgress(int maxProgress) {
        this.maxProgress = maxProgress;
    }

    public int getProgress() {
        return progress;
    }

    public void setProgress(int progress) {
        if (this.progress != progress) {
            this.progress = progress;
            if (!CALLED_FROM_ANGLE) {
                int newPercent = (this.progress * 100) / this.maxProgress;
                int newAngle = (newPercent * 360) / 100;
                this.setAngle(newAngle);
                this.setProgressPercent(newPercent);
            }
            mListener.onProgressChange(this, this.getProgress());
            CALLED_FROM_ANGLE = false;
        }
    }

    public int getProgressPercent() {
        return progressPercent;
    }

    public void setProgressPercent(int progressPercent) {
        this.progressPercent = progressPercent;
    }

    public void setRingBackgroundColor(int color) {
        circleRing.setColor(color);
    }

    public void setBackGroundColor(int color) {
        innerColor.setColor(color);
    }

    public void setProgressColor(int color) {
        circleColor.setColor(color);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        boolean up = false;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                moved(x, y, up);
                break;
            case MotionEvent.ACTION_MOVE:
                moved(x, y, up);
                break;
            case MotionEvent.ACTION_UP:
                up = true;
                moved(x, y, up);
                break;
        }
        return true;
    }

    private void moved(float x, float y, boolean up) {
        float distance = (float) Math.sqrt(Math.pow((x - cx), 2) + Math.pow((y - cy), 2));
        if (distance < outerRadius + adjustmentFactor && distance > innerRadius - adjustmentFactor && !up) {
            IS_PRESSED = true;

            markPointX = (float) (cx + outerRadius * Math.cos(Math.atan2(x - cx, cy - y) - (Math.PI / 2)));
            markPointY = (float) (cy + outerRadius * Math.sin(Math.atan2(x - cx, cy - y) - (Math.PI / 2)));

            float degrees = (float) ((float) ((Math.toDegrees(Math.atan2(x - cx, cy - y)) + 360.0)) % 360.0);
            // and to make it count 0-360
            if (degrees < 0) {
                degrees += 2 * Math.PI;
            }

            setAngle(Math.round(degrees));
            invalidate();

        } else {
            IS_PRESSED = false;
            invalidate();
        }

    }

    public float getAdjustmentFactor() {
        return adjustmentFactor;
    }

    public void setAdjustmentFactor(float adjustmentFactor) {
        this.adjustmentFactor = adjustmentFactor;
    }

    public void ShowSeekBar() {
        SHOW_SEEKBAR = true;
    }

    public void hideSeekBar() {
        SHOW_SEEKBAR = false;
    }
}
  

现在在您的活动中使用

     

layout.activity_main

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/rootView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <neel.com.demo.CircularSeekBar
        android:id="@+id/circularSeekbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:padding="10dp" />

</LinearLayout>
  

活动代码

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {
    CircularSeekBar circularSeekbar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        circularSeekbar = findViewById(R.id.circularSeekbar);

        circularSeekbar.setMaxProgress(100);
        circularSeekbar.setBarWidth(20);

        // if you want to change thumb icon that try below method
        //  circularSeekbar.setBitmapThumbIcon(R.mipmap.ic_fav_round);

        circularSeekbar.invalidate();

        circularSeekbar.setSeekBarChangeListener(new CircularSeekBar.OnSeekChangeListener() {
            @Override
            public void onProgressChange(CircularSeekBar view, int newProgress) {
                Log.e("Seekbar_Progress", String.valueOf(view.getProgress()));
            }
        });

    }
}
  

输出

enter image description here

注意

如果您想使用任何库,请点击以下链接

答案 1 :(得分:2)

请遵循以下说明:

将此库添加到您的项目中(等级):

implementation 'com.github.Triggertrap:SeekArc:v1.1'

请注意,由于存在错误,您需要在清单中的application标记下添加以下行:

tools:replace="android:icon"

在活动布局中添加视图: 我会根据您的喜好准备参数:

<com.triggertrap.seekarc.SeekArc
        android:id="@+id/seekArc"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:padding="30dp"
        app:progress="20"
        app:arcColor="#00ffffff"
        app:thumb="@drawable/ic_androido"
        app:startAngle="0"
        app:sweepAngle="360"
        app:touchInside="false"
        android:focusable="false"
        android:clickable="false"/>

将此可绘制对象添加到您的资源/可绘制对象(ic_androido.xml)中:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="42dp"
android:height="42dp"
android:viewportWidth="413.137"
android:viewportHeight="413.137">

<path
    android:fillColor="#AAC148"
    android:pathData="M311.358,136.395H101.779c-4.662,0-8.441,3.779-8.441,8.441v175.749 c0,4.662,3.779,8.441,8.441,8.441h37.363v59.228c0,13.742,11.14,24.883,24.883,24.883l0,0c13.742,0,24.883-11.14,24.883-24.883 v-59.228h34.803v59.228c0,13.742,11.14,24.883,24.883,24.883l0,0c13.742,0,24.883-11.14,24.883-24.883v-59.228h37.882 c4.662,0,8.441-3.779,8.441-8.441V144.836C319.799,140.174,316.02,136.395,311.358,136.395z" />
<path
    android:fillColor="#AAC148"
    android:pathData="M57.856,136.354L57.856,136.354c-13.742,0-24.883,11.14-24.883,24.883v101.065 c0,13.742,11.14,24.883,24.883,24.883l0,0c13.742,0,24.883-11.14,24.883-24.883V161.237 C82.738,147.495,71.598,136.354,57.856,136.354z" />
<path
    android:fillColor="#AAC148"
    android:pathData="M355.281,136.354L355.281,136.354c-13.742,0-24.883,11.14-24.883,24.883v101.065 c0,13.742,11.14,24.883,24.883,24.883l0,0c13.742,0,24.883-11.14,24.883-24.883V161.237 C380.164,147.495,369.024,136.354,355.281,136.354z" />
<path
    android:fillColor="#AAC148"
    android:pathData="M103.475,124.069h205.692c5.366,0,9.368-4.943,8.266-10.195 c-6.804-32.428-27.45-59.756-55.465-75.543l17.584-31.727c1.19-2.148,0.414-4.855-1.734-6.045 c-2.153-1.193-4.856-0.414-6.046,1.734l-17.717,31.966c-14.511-6.734-30.683-10.495-47.734-10.495 c-17.052,0-33.224,3.761-47.735,10.495L140.869,2.292c-1.191-2.149-3.898-2.924-6.045-1.734c-2.148,1.19-2.924,3.897-1.734,6.045 l17.584,31.727c-28.015,15.788-48.661,43.115-55.465,75.544C94.106,119.126,98.108,124.069,103.475,124.069z M267.697,76.786 c0,5.282-4.282,9.565-9.565,9.565c-5.282,0-9.565-4.282-9.565-9.565c0-5.282,4.282-9.565,9.565-9.565 C263.415,67.221,267.697,71.504,267.697,76.786z M154.508,67.221c5.282,0,9.565,4.282,9.565,9.565c0,5.282-4.282,9.565-9.565,9.565 c-5.282,0-9.565-4.282-9.565-9.565C144.943,71.504,149.225,67.221,154.508,67.221z" />

Activity类中的代码:

        SeekArc seekArc;
    int progress = 0;
    seekArc = (SeekArc) findViewById(R.id.seekArc);

    final Handler handler = new Handler();
    handler.postDelayed(new Runnable(){
        public void run(){
            seekArc.setProgress(progress++);
            if (progress >= 100) {
                handler.removeCallbacks(this);
                return;
            }
            handler.postDelayed(this, 300);
        }
    }, 50);

enter image description here

https://github.com/neild001/SeekArc的图书馆赠送金额

图标归功于https://www.flaticon.com/free-icon/android_174836#term=android&page=1&position=1