精华内容
下载资源
问答
  • 最近的小程序项目个设计图要求做一...于是我决定做个动态! 在mdn把新特性gradients(渐变)、transitions(过渡)、 animations(动画) 都看了一遍,不禁感叹css牛逼!这三个新特性加上canvas,仿佛一瞬间了正面刚js...
        

    最近的小程序项目有个设计图要求做一个圆环,两种颜色分配,分别代表可用金额和冻结金额。要是就直接这么显示,感觉好像挺没水平??于是我决定做个动态!

    在mdn把新特性gradients(渐变)、transitions(过渡)、 animations(动画) 都看了一遍,不禁感叹css牛逼!这三个新特性加上canvas,仿佛一瞬间有了正面刚js的能耐。用js很难过渡得那么完美,而且浏览器的css渲染明显比用js性能好得多。
    然后看了张鑫旭(传说中玩转css的那个男人)的一篇关于圆环的博文,拍案叫绝。链接=>3种纯CSS实现中间镂空的12色彩虹渐变圆环方法
    只能说服气!除了灵活运用各种css特性之外,鑫大佬最让我佩服的是他的创造性思维。会让你不禁感叹:卧槽,还有这种操作?!想到了高中物理老师每次装完逼讲的一句话:思想有多远,就能走多远。

    虽然demo跟我的需求不太一样,问题还是没有解决,但我认真看完之后还是学会了很多,对我后面的代码帮助很大。鑫大佬这篇博文的重点还是在渐变,而我需要动态平缓连续得实现颜色的分配,比如原本整个环是绿色,然后慢慢地60%被红色占了,而且整个过程要平滑。跟我的需求最接近的就是倒计时那个demo,linear-gradient线性渐变实现的多彩圆环demo,但不是连续的过程,而是通过剪裁,每次剪30度。

    看了其他一些博客分享,好像也没有找到合适的,那没办法了...只能自己想一个!
    因为再写这个demo的时候,发现小程序和H5在css表现上还是有些差异(具体有哪些差异,在文末总结),所以还是贴H5代码好了。

    不多说,直接上代码


    代码部分

    //html部分
    <div class="circle">
      <div class="circle-left"></div>
      <div class="circle-right"></div>
      <div class="circle-bottom-left"></div>
      <div class="circle-bottom-right"></div>
    </div>
    <div class="info">¥4500/¥5000</div>
    //css部分
    .circle {
      -webkit-mask: radial-gradient(transparent 150px, #000 150px);
      width: 400px;
      height: 400px;
      overflow: hidden;
      border-radius: 50%;
      position: relative;
    }
    
    .circle-left {
      width: 50%;
      height: 100%;
      background: #24B39B;
      transform-origin: 100% 50%;
      position: absolute;
      left: 0;
      z-index: 0;
    }
    
    .circle-right {
      width: 50%;
      height: 100%;
      background: #24B39B;
      transition: transform 1s linear;
      transform-origin: 0% 50%;
      position: absolute;
      right: 0;
      z-index: 2;
    }
    
    .circle-bottom-left {
      width: 50%;
      height: 100%;
      background: rgb(234, 67, 15);
      position: absolute;
      left: 0;
      z-index: -1;
    }
    
    .circle-bottom-right {
      width: 50%;
      height: 100%;
      background: rgb(234, 67, 15);
      position: absolute;
      right: 0;
      z-index: 1;
    }
    
    .info {
      width: 400px;
      height: 400px;
      line-height: 400px;
      text-align: center;
      margin-top: -400px;  
    }
    //js代码
    window.onload = function () {
      var red = 4500, total = 5000 //红色区域代表的金额和总金额
      var percent = red / total
      var right = document.getElementsByClassName('circle-right')[0]
      var left = document.getElementsByClassName('circle-left')[0]
      if (percent <= 0.5) {  //红色区域不超过一半
        right.style.transform = `rotate(${percent * 360}deg)`
      } else {    //红色区域超过一半的情况,重点部分
        right.style.transform = `rotate(180deg)`
        right.style.transition = `opacity 0s step-end 1s, transform 1s linear` //timing-function需要设为linear来达到视觉上的平缓过渡
        right.style.opacity = 0
    
        left.style.transition = `transform ${(percent - 0.5) / 0.5}s linear 1s`
        left.style.transform = `rotate(${percent * 360 - 180}deg)`
      }
    }

    效果图
    图片描述

    思路

    st=>start: 开始
    e=>end: 结束
    con=>condition: degree<=180?
    op1=>operation: 右绿旋转
    op2=>operation: 右绿旋转180度,opacity变为0,然后左绿旋转
    
    st->con
    con(yes)->op1->e
    con(no)->op2->e

    难点在于红色区域大于一半的情况,左右绿色半圆的衔接,过渡要自然,不能让人看出什么明显的破绽。
    **这种情况我的做法是:4个半圆(红绿各两个)的z-index设为左红<左绿<右红<右绿
    两个绿半圆的transform的time-function(时间函数)统一设为linear(线性)。右绿旋转180度(1秒)后opacity立即变成0(时间函数step-end),这样就不会挡住左红露出。然后左绿开始转(transform延迟1秒执行,因为要等待右绿转完),它转的时间要根据度数动态控制,比如总共要转270度,右绿转了180度,所以左绿只需要转90度。这就好办了,为了保持右绿的旋转速度,时间和度数要成比例,右绿转180度用1s,左绿转90度只能用0.5s

    优点

    1. 不需要js代码动态实现动画(js只用来计算度数和触发transition)
    2. 因为对js几乎没什么依赖,浏览器内核直接渲染,性能较好,过渡自然
    3. 代码量很少
    

    不足

    1. 因为是css3的属性,兼容不会太好
    2. 时间函数只能用线性linear,用默认的ease(不匀速)会衔接不上
    3. 只能两种颜色分布,再加一种的话行不通
    

    有更好办法实现相同效果的大佬,欢迎留言!

    问题探究&解决

    虽然效果图gif画质有点感人,但还是可以发现一个问题:内环边缘明显很粗糙!这个要怎么解决呢?
    中间这个透明遮罩的代码是`-webkit-mask: radial-gradient(transparent 150px, #000 150px);
    我的做法就是把transparent 150px改成transparent 148px,就是说空出一两个像素点,让粗糙的部分虚化。
    至于为什么会出现粗糙,额。。。我觉得是150px这一层上了太多颜色,加上本来画弧圈就没有防锯齿处理,色素点可能会拥挤,加剧了锯齿状这种效果。具体是什么原因,或者有更好的解决办法,欢迎大佬指教。

    修改后的效果图
    图片描述
    是不是明显好很多~

    上文提到的小程序的css和h5的差异,经过再一次的实验,发现不是小程序内核渲染的问题,应该是微信开发者工具显示的问题。。。希望尽快能得到改善,不然对开发人员影响挺大的

    clipboard.png这个info的盒子margin-top负数在工具中显示翻不上去,但内容50000上去了.
    clipboard.png过了几秒再点(啥都没干),这个info的盒子又跑到这里来

    clipboard.png为了验证这个info的盒子到底有没有上去,我加了一个红色的盒子,发现并没有被info盒子挤掉

    clipboard.png取消info的margin-top属性,红色的盒子被挤掉,内容50000也下来了

    终于!!!原来都是开发者工具摆的乌龙,其实info盒子一直在上面,只是调试不能正常显示他的位置。。。

    话说回来,小程序不能获取DOM节点操作DOM,突然觉得只能数据驱动,不能操作DOM节点有时也挺麻烦的,transition那些需要动态改的样式只能写到style了。。。

    最后,看好小程序,希望各种问题能尽快完善,越来越好。

    展开全文
  • Android 布局圆角方案总结

    千次阅读 2020-06-09 23:57:04
    这里和图片切圆角一些雷同的地方,可以相互借鉴,但是也不不全一样。图片切圆角的一些总结和实践准备下次有空再写出来。 假设我们要对一种LinearLayout布局切圆角,不知道你能想出来哪些办法。我这里先提供下我...

    一.遇到的问题

        最近在开发中会遇到Android布局切圆角的需求,大多数是对一个layout布局切下圆角。这里和图片切圆角有一些雷同的地方,可以相互借鉴,但是也不全一样。图片切圆角的一些总结和实践准备下次有空再写出来。

        假设我们要对一种LinearLayout布局切圆角,不知道你能想出来哪些办法。我这里先提供下我的思路,亲自实践过的主要包括下面五种:

       1.利用xml背景文件配置shape属性实现切圆角

       2.利用GradientDrawable实现切圆角

       3.利用clipPath实现切圆角

       4.利用CardView实现切圆角

        5.利用ViewOutlineProvider实现切圆角

    下面分别对这几种方式进行实践与优缺点分析,原理讲解较少,感兴趣可以自己再深入了解下。

     

    二.利用xml背景文件配置shape属性实现

    2.1方案实现

        这种方式相比大家都比较熟悉了,就是在drawable文件夹下新建一个App Action XML File,然后作为background属性添加到Layout XML File中。

        如果要生成四个角都为10dp圆角的形状我们可以在drawable文件夹下新建如下shape_10dp_corners.xml文件

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
        <corners android:radius="10dp" />
        <solid android:color="@color/colorAccent" />
    </shape>

        如果仅要生成上方两个10dp圆角的形状我们可以在drawable文件夹下新建如下shape_10dp_top_corners.xml文件

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
        <solid android:color="@color/colorPrimary"/>
        <corners
            android:topLeftRadius="10dp"
            android:topRightRadius="10dp" />
    </shape>

        如果仅要生成上方两个10dp圆角的形状我们可以在drawable文件夹下新建如下shape_10dp_bottom_right_corners.xml文件

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
        <corners android:bottomRightRadius="10dp"/>
        <solid android:color="@color/colorAccent"/>
    </shape>

        然后在XmlShapeActivity的布局XML文件中为对应的三个LinearLayout分别设置background属性如下

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".xmlshape.XmlShapeActivity">
    
        <LinearLayout
            android:id="@+id/lly1"
            android:layout_width="0dp"
            android:layout_height="100dp"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:layout_marginTop="16dp"
            android:layout_marginEnd="16dp"
            android:layout_marginRight="16dp"
            android:background="@drawable/shape_10dp_corners"
            android:orientation="vertical"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="四个圆角10dp"
                android:textSize="24sp"
                android:textColor="#FFFFFF" />
        </LinearLayout>
    
        <LinearLayout
            android:id="@+id/lly2"
            android:layout_width="0dp"
            android:layout_height="100dp"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:layout_marginTop="16dp"
            android:layout_marginEnd="16dp"
            android:layout_marginRight="16dp"
            android:background="@drawable/shape_10dp_top_corners"
            android:orientation="vertical"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/lly1" >
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="上方圆角10dp"
                android:textSize="24sp"
                android:textColor="#FFFFFF"/>
        </LinearLayout>
    
        <LinearLayout
            android:id="@+id/lly3"
            android:layout_width="0dp"
            android:layout_height="100dp"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:layout_marginTop="16dp"
            android:layout_marginEnd="16dp"
            android:layout_marginRight="16dp"
            android:background="@drawable/shape_10dp_bottom_right_corners"
            android:orientation="vertical"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/lly2" >
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="右下圆角10dp"
                android:textSize="24sp"
                android:textColor="#FFFFFF"/>
        </LinearLayout>
    </androidx.constraintlayout.widget.ConstraintLayout>

        然后勉为其难地贴一下打酱油的XmlShapeActivity代码

    package com.openld.roundcornerdemmo.xmlshape;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    import android.os.Bundle;
    
    import com.openld.roundcornerdemmo.R;
    
    public class XmlShapeActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_xml_shape);
        }
    }

    2.1运行效果

    2.3优缺点

    优点:

    1.最简单

    2.四个圆角可以单独定制,也可以统一处理

    3.还可以一并设置背景颜色等属性

    4.圆角无锯齿

     

    缺点:

    1.圆角属性是提前确定的并写在客户端本地的drawable中,如果是动态下发再去配置的话这种方式无法支持。

    2.如果子View长宽都match_parent并且设置一个有色background属性的话,圆角就消失了。

     

    三.利用GradientDrawable实现切圆角

    3.1 方案实现

        有没有想过动态地在Java代码中为layout设置圆角,那就想到了GradientDrawable。其实是和第一种XML方式呼应的,只不过没有办法让你你动态去配置XML,那就用GradientDrawable吧,XML shape中能设置的属性这里也能哦!

        这里先贴一下GradientDrawableActivity的布局XML

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".gradientdrawable.GradientDrawableActivity">
    
        <LinearLayout
            android:id="@+id/lly1"
            android:layout_width="0dp"
            android:layout_height="100dp"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:layout_marginTop="16dp"
            android:layout_marginEnd="16dp"
            android:layout_marginRight="16dp"
            android:orientation="vertical"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="四个圆角10dp"
                android:textSize="24sp" />
        </LinearLayout>
    
        <LinearLayout
            android:id="@+id/lly2"
            android:layout_width="0dp"
            android:layout_height="100dp"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:layout_marginTop="16dp"
            android:layout_marginEnd="16dp"
            android:layout_marginRight="16dp"
            android:orientation="vertical"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/lly1">
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="左边两个10dp圆角"
                android:textSize="24sp" />
        </LinearLayout>
    
        <LinearLayout
            android:id="@+id/lly3"
            android:layout_width="0dp"
            android:layout_height="100dp"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:layout_marginTop="16dp"
            android:layout_marginEnd="16dp"
            android:layout_marginRight="16dp"
            android:orientation="vertical"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/lly2">
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="左上角10dp圆角"
                android:textSize="24sp" />
        </LinearLayout>
    </androidx.constraintlayout.widget.ConstraintLayout>

        再贴一下GradientDrawableActivity的代码

    package com.openld.roundcornerdemmo.gradientdrawable;
    
    import android.graphics.drawable.GradientDrawable;
    import android.os.Bundle;
    import android.widget.LinearLayout;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    import com.openld.roundcornerdemmo.R;
    import com.openld.roundcornerdemmo.utils.DisplayUtils;
    
    public class GradientDrawableActivity extends AppCompatActivity {
        private LinearLayout mLly1;
    
        private LinearLayout mLly2;
    
        private LinearLayout mLly3;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_gradient_drawable);
    
            initWidgets();
        }
    
        private void initWidgets() {
            mLly1 = findViewById(R.id.lly1);
            GradientDrawable gradientDrawable1 = new GradientDrawable();
            gradientDrawable1.setShape(GradientDrawable.RECTANGLE);
            gradientDrawable1.setCornerRadius(DisplayUtils.dp2px(this, 10F));
            gradientDrawable1.setColor(getResources().getColor(R.color.colorPrimary));
            mLly1.setBackground(gradientDrawable1);
    
            mLly2 = findViewById(R.id.lly2);
            GradientDrawable gradientDrawable2 = new GradientDrawable();
            gradientDrawable2.setShape(GradientDrawable.RECTANGLE);
            gradientDrawable2.setColor(getResources().getColor(R.color.colorAccent));
            float[] radii = new float[]{
                    DisplayUtils.dp2px(this, 10F), DisplayUtils.dp2px(this, 10F),
                    0F, 0F,
                    0F, 0F,
                    DisplayUtils.dp2px(this, 10F), DisplayUtils.dp2px(this, 10F)
            };
            gradientDrawable2.setCornerRadii(radii);
            mLly2.setBackground(gradientDrawable2);
    
            mLly3 = findViewById(R.id.lly3);
            GradientDrawable gradientDrawable3 = new GradientDrawable();
            gradientDrawable3.setShape(GradientDrawable.RECTANGLE);
            gradientDrawable3.setColor(getResources().getColor(R.color.colorPrimary));
            float[] radii1 = new float[]{
                    DisplayUtils.dp2px(this, 10F), DisplayUtils.dp2px(this, 10F),
                    0F, 0F,
                    0F, 0F,
                    0F, 0F
            };
            gradientDrawable3.setCornerRadii(radii1);
            mLly3.setBackground(gradientDrawable3);
        }
    }

        核心代码都在initWidgets()方法中,里面实例化一个GradientDrawable对象,设置一下对应的属性。setCornerRadii()或者se'tCornerRadius()方法即为设置圆角,具体可以看下API,这里不再赘述。

     

    3.2 运行效果

     

    3.3 优缺点

    优点:

    1.支持动态配置啊

    2.四个圆角可以单独定制,也可以统一处理

    3.还可以一并设置背景颜色等属性

    4.圆角无锯齿

     

    缺点

    1.如果子View长宽都match_parent并且设置一个有色background属性的话,圆角就消失了。

     

    四. 利用clipPath实现切圆角

    4.1 方案实现

        继承现有的布局,在draw()方法中使用clipPath()方法切割你想要的形状,只要切割出一个圆角矩形即可。

    比如新建一个继承LinearLayout的CornersLinearLayout

    package com.openld.roundcornerdemmo.clippath;
    
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Canvas;
    import android.graphics.Path;
    import android.graphics.RectF;
    import android.util.AttributeSet;
    import android.widget.LinearLayout;
    
    import androidx.annotation.Nullable;
    
    import com.openld.roundcornerdemmo.R;
    
    /**
     * author: lllddd
     * created on: 2020/6/9 10:55
     * description:
     */
    public class CornersLinearLayout extends LinearLayout {
        private Context mContext;
    
        private float mCorners;
        private float mLeftTopCorner;
        private float mRightTopCorner;
        private float mLeftBottomCorner;
        private float mRightBottomCorner;
    
        private int mWidth;
        private int mHeight;
    
        public CornersLinearLayout(Context context) {
            this(context, null);
        }
    
        public CornersLinearLayout(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public CornersLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
    
            mContext = context;
    
            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CornersLinearLayout);
            mCorners = typedArray.getDimension(R.styleable.CornersLinearLayout_corner, 0F);
            mLeftTopCorner = typedArray.getDimension(R.styleable.CornersLinearLayout_leftTopCorner, 0F);
            mRightTopCorner = typedArray.getDimension(R.styleable.CornersLinearLayout_rightTopCorner, 0F);
            mRightBottomCorner = typedArray.getDimension(R.styleable.CornersLinearLayout_rightBottomCorner, 0F);
            mLeftBottomCorner = typedArray.getDimension(R.styleable.CornersLinearLayout_leftBottomCorner, 0F);
            typedArray.recycle();
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            mWidth = getMeasuredWidth();
            mHeight = getMeasuredHeight();
            setMeasuredDimension(mWidth, mHeight);
        }
    
        @Override
        public void draw(Canvas canvas) {
            canvas.save();
    
            Path path = new Path();
            RectF rectF = new RectF(0, 0, mWidth, mHeight);
            if (mCorners > 0F) {
                path.addRoundRect(rectF, mCorners, mCorners, Path.Direction.CCW);
            } else {
                float[] radii = new float[]{
                        mLeftTopCorner, mLeftTopCorner,
                        mRightTopCorner, mRightTopCorner,
                        mRightBottomCorner, mRightBottomCorner,
                        mLeftBottomCorner, mLeftBottomCorner
                };
                path.addRoundRect(rectF, radii, Path.Direction.CCW);
            }
            canvas.clipPath(path);
    
            super.draw(canvas);
        }
    }
    

        相关的属性声明在style.xml文件中配置,这里不涉及。draw()方法是在onDraw()之前的一个方法,在这里可以使用clipPath()裁剪出一个圆角矩形轮廓。调用canvas.clipPath(path)生效,之后再执行super方法即可。

     

    4.2 运行效果

     

    4.3 优缺点

    优点:

    1.整个外层轮廓都切成圆角矩形,子View的background不会影响到圆角

    2.四个角可以单独配置也可以统一配置

     

    缺点:

    1.无法抗锯齿,在一些老的手机上面效果可能差强人意

    2.需要实现自己的自定义布局

     

    五.利用CardView实现切圆角

    5.1 方案实现

        CardView是v7包中的组件(ViewGroup),主要用来设置布局的边框为圆角、z轴的偏移量(这个是5.0以后才有的概念,也就是阴影的效果)。这里我们仅仅使用圆角功能。

        实现一个带CardView的布局

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".cardview.CardViewActivity">
    
        <androidx.cardview.widget.CardView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:layout_marginTop="16dp"
            android:layout_marginEnd="16dp"
            android:layout_marginRight="16dp"
            android:layout_marginBottom="16dp"
            app:cardBackgroundColor="@color/colorAccent"
            app:cardCornerRadius="10dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="10dp的圆角"
                android:textColor="#FFFFFF"
                android:textSize="24sp" />
        </androidx.cardview.widget.CardView>
    
    </androidx.constraintlayout.widget.ConstraintLayout>

        对应activity的代码就没必要贴了,XML布局中

    app:cardCornerRadius="10dp"

        即配置四个角为10dp的圆角

     

    5.2 运行效果

     

    5.3 优缺点

    优点:

    1.Google的控件,效果和稳定性都是杠杠的,还支持阴影等其他的配置

    2.抗锯齿

     

    缺点:

    1.四个角要一起配置,不支持其中若干个角单独配置

    2.使用时外层要嵌套CardView

     

    六.利用ViewOutlineProvider实现切圆角

    6.1 方案实现

        利用ViewOutlineProvider设置轮廓切圆角。

        对应ViewOutlineProviderActivity的布局XML如下

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".viewoutlineprovider.ViewOutlineProviderActivity">
    
        <LinearLayout
            android:id="@+id/lly"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:layout_marginTop="16dp"
            android:layout_marginEnd="16dp"
            android:layout_marginRight="16dp"
            android:layout_marginBottom="16dp"
            android:orientation="vertical"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:background="@color/colorAccent">
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="10dp的圆角"
                android:textSize="24sp" />
        </LinearLayout>
    </androidx.constraintlayout.widget.ConstraintLayout>

        ViewOutlineProviderActivity代码如下

    package com.openld.roundcornerdemmo.viewoutlineprovider;
    
    import android.graphics.Outline;
    import android.os.Build;
    import android.os.Bundle;
    import android.view.View;
    import android.view.ViewOutlineProvider;
    import android.widget.LinearLayout;
    
    import androidx.annotation.RequiresApi;
    import androidx.appcompat.app.AppCompatActivity;
    
    import com.openld.roundcornerdemmo.R;
    import com.openld.roundcornerdemmo.utils.DisplayUtils;
    
    public class ViewOutlineProviderActivity extends AppCompatActivity {
        private LinearLayout mLly;
    
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_view_outline_provider);
    
            initWidgets();
        }
    
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        private void initWidgets() {
            mLly = findViewById(R.id.lly);
    
            mLly.setOutlineProvider(new ViewOutlineProvider() {
                @Override
                public void getOutline(View view, Outline outline) {
                    outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), DisplayUtils.dp2px(ViewOutlineProviderActivity.this, 10F));
                }
            });
            mLly.setClipToOutline(true);
        }
    }

        关键就是mLly.setOutlineProvider()与mLly.setClipToOutline()两个方法的使用。

     

    6.2 运行效果

     

    6.3 优缺点

    优点:

    1.支持动态下发与配置

     

    缺点:

    1.只能四个角同时配置不支持每个角单独配置

     

    七.源码位置

    链接地址

     

    展开全文
  • 圆环百分比分布图

    千次阅读 2017-11-14 17:04:33
    时候我们会遇到做百分比分布图的功能,这样的功能说难不难,说容易也不容易,主要是对绘制api和动画掌握的较为熟悉就行,下面就试试做下这个功能首先看个效果图:一、分析实现这个效果要做哪些事:1.画一个底部灰色...

    有时候我们会遇到做百分比分布图的功能,这样的功能说难不难,说容易也不容易,主要是对绘制api和动画掌握的较为熟悉就行,下面就试试做下这个功能

    首先看个效果图:这里写图片描述

    一、分析

    实现这个效果要做哪些事:

    1.画一个底部灰色圆
    2.画绿色圆弧
    3.画红褐色圆弧

    动态圆弧的实现可以通过动画来实现,首先是绿色圆弧,只需要动态增加圆弧所需要经过的弧度即可,红褐色圆弧则就需要将起点设置在绿圆弧的结束位置,结束点在绿圆弧的起点角度

    二、实现

    按照上面的思路来做,第一步先画一个圆底,只需要一句话即可:

    canvas.drawCircle(getWidth()/2,getHeight()/2,getWidth()/2-paintSize, cyclePaint);

    然后做第二步画绿色圆弧,代码如下:

    canvas.drawArc(rectF, 180, sweepAngle, false, paintFirst);
    

    sweepAngle是需要动态改变的,通过动画实现:

      public void startAnimation(){
            ValueAnimator animator=ValueAnimator.ofFloat(0,progress);
            animator.setDuration(1500);
            animator.setInterpolator(new AccelerateInterpolator());
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float progress= (float) animation.getAnimatedValue();
                    sweepAngle=progress*360/100;
                    postInvalidate();
                    if (sweepAngle>=progress*360/100) {
                        startFreezeAnimation();
                    }
    
                }
            });
            animator.start();
        }

    接下来是第三步画红褐色圆弧,根据上面的分析,代码如下:

    canvas.drawArc(rectF, progress * 360 / 100 + 180, sweepSecondAngle, false, paintSecond);

    其中progress 是绿色圆弧的百分比,默认的是50,sweepSecondAngle则是动态改变的需要经过的角度,但是褐色圆弧需要在绿色圆弧完成之后在开始,所以上面代码中的

      if (sweepAngle>=progress*360/100) {
             startFreezeAnimation();
       }

    就是起到这个作用的,startFreezeAnimation()代码如下:

      public void startFreezeAnimation(){
            ValueAnimator animator=ValueAnimator.ofFloat(0,100-progress);
            animator.setDuration(1500);
            animator.setInterpolator(new DecelerateInterpolator());
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float progress= (float) animation.getAnimatedValue();
                    sweepSecondAngle =progress*360/100;
                    postInvalidate();
                }
            });
            animator.start();
        }

    这样就实现了上面的效果了。下面是完整的代码:

    public class MyCyclePercentView extends View {
    
        private int color=0xff5ce6cf;
        private Paint cyclePaint;
        private Paint paintFirst;
    
        private Paint paintSecond;
    
        private float progress=50;
        private float sweepAngle;
        private float sweepSecondAngle;
    
        private RectF rectF;
    
        private float paintSize;
    
        public MyCyclePercentView(Context context) {
            super(context);
        }
    
        public MyCyclePercentView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            initPaint(context);
        }
    
        public MyCyclePercentView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
    
        private void initPaint(Context context){
            paintSize= DensityUtil.dip2px(context,30);
    
            cyclePaint=getPaint(0xffaaaaaa);
            paintFirst=getPaint(0xff5ce6cf);
            paintSecond=getPaint(0xfffe7465);
        }
    
        private Paint getPaint(int color){
            Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG);
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeWidth(paintSize);
            paint.setColor(color);
            return paint;
    
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawCircle(getWidth()/2,getHeight()/2,getWidth()/2-paintSize, cyclePaint);
            paintFirst.setColor(color);
            canvas.drawArc(rectF, 180, sweepAngle, false, paintFirst);
            canvas.drawArc(rectF, progress * 360 / 100 + 180, sweepSecondAngle, false, paintSecond);
    
    
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            rectF=new RectF(paintSize,paintSize,w-paintSize,h-paintSize);
        }
    
        public void startAnimation(){
            ValueAnimator animator=ValueAnimator.ofFloat(0,progress);
            animator.setDuration(1500);
            animator.setInterpolator(new AccelerateInterpolator());
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float progress= (float) animation.getAnimatedValue();
                    sweepAngle=progress*360/100;
                    postInvalidate();
    
                    if (sweepAngle>=progress*360/100) {
                        startFreezeAnimation();
                    }
                }
            });
            animator.start();
        }
        public void startFreezeAnimation(){
            ValueAnimator animator=ValueAnimator.ofFloat(0,100-progress);
            animator.setDuration(1500);
            animator.setInterpolator(new DecelerateInterpolator());
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float progress= (float) animation.getAnimatedValue();
                    sweepSecondAngle =progress*360/100;
                    postInvalidate();
                }
            });
            animator.start();
        }
    
    
        public void setProgress(float progress) {
            this.progress = progress;
            postInvalidate();
        }
    }
    展开全文
  • Android自动手绘,你儿时画家梦!

    千次阅读 多人点赞 2016-05-27 20:02:46
    我的简书同步发布Android自动手绘,你儿时画家梦! 从小就喜欢到处乱画,家里一米以下墙上就没有一块干净的地方(那是老房子啦~)~~(⊙﹏⊙)b。好了,废话不多说,进入主题。今天主要跟大家分享一下如何将一张图片...

    我的简书同步发布Android自动手绘,圆你儿时画家梦!

    从小就喜欢到处乱画,家里一米以下墙上就没有一块干净的地方(那是老房子啦~)~~(⊙﹏⊙)b。好了,废话不多说,进入主题。今天主要跟大家分享一下如何将一张图片转成手绘效果,并模拟画家动态绘制。先把最终效果图亮出来,觉得好的请点个赞,您的点赞是对我的最大鼓励(O(∩_∩)O哈哈~)。
    效果图如下:
    最终效果
    心动有木有!

    原理

    大概介绍一下实现原理。首先你得有一张图(废话~),接下来就是把这张图的轮廓提取出来,轮廓提取算法有很多,本人不是搞图像处理的,对图像处理感兴趣的童鞋可以查看相关资料。如果你有好的轮廓提取算法,也可以把源码中的算法替换掉,我们采用的轮廓提取算法是Sobel边缘检测。网上的实现有很多,我懒得去实现一遍,github有开源的实现,直接下载了:GraphicLib.本文不具体介绍轮廓提取算法。得到轮廓图以后,接下来要做的是,按照线条的走势,一个一个像素点绘制出来。注意,一定要按照线条的走势显示对应的像素点,如果用两个嵌套的for循环,动画会像网速不好时浏览器显示图片一样。难点就在于此,如何把像素点按照线条方向绘制。接下来我们一起研究。

    代码实现

    如何让绘制的点不会跳远太远,使之连贯起来?首先,对于一个刚绘制完成的点,接下来要绘制的点肯定要选择离它最近的点,这样肯定是最佳的下一个绘制点。因此,只要我们找到最近的点就可以,寻找最近的点,可以通过以圆的方式不断改变半径的大小进行探测。但是用圆的话需要各种三角函数运算,影响效率。我们可以换一种方法:根据当前的点可以轻松得到每一层以该点为中心的正方形,一层层遍历,直到找到需要的点就是我们要的点。遍历的方法很简单,就是比较对应的正方形的上下左右四条边上面的像素点。如下图所示:

    寻找附近点

    接下来看看如何用代码去实现寻找最近的点:

     //获取离指定点最近的一个未绘制过的点
        private Point getNearestPoint(Point p) {
            if (p == null) return null;
            //以点p为中心,向外扩大搜索范围,每次搜索的是与p点相距add的正方形
            for (int add = 1; add < mSrcBmWidth && add < mSrcBmHeight; add++) {
                //
                int beginX = (p.x - add) >= 0 ? (p.x - add) : 0;
                int endX = (p.x + add) < mSrcBmWidth ? (p.x + add) : mSrcBmWidth - 1;
                int beginY = (p.y - add) >= 0 ? (p.y - add) : 0;
                int endY = (p.y + add) < mSrcBmHeight ? (p.y + add) : mSrcBmHeight - 1;
                //搜索正方形的上下边
                for (int x = beginX; x <= endX; x++) {
                    if (mArray[x][beginY]) {
                       //标记当前点已经访问过
                        mArray[x][beginY] = false;
                        return new Point(x, beginY);
                    }
                    if (mArray[x][endY]) {
                        //标记当前点已经访问过
                        mArray[x][endY] = false;
                        return new Point(x, endY);
                    }
                }
                //搜索正方形的左右边
                for (int y = beginY + 1; y <= endY - 1; y++) {
                    if (mArray[beginX][y]) {
                        //标记当前点已经访问过
                        mArray[beginX][beginY] = false;
                        return new Point(beginX, beginY);
                    }
                    if (mArray[endX][y]) {
                       //标记当前点已经访问过
                        mArray[endX][y] = false;
                        return new Point(endX, y);
                    }
                }
            }
    
            return null;
        }

    任何一个点,只要还存在没有绘制过的点,就一定能找得到与它最近的点,如果找不到,说明所有的点已经绘制完毕。为了防止查找到重复的点,需要把访问过的点做上记号(即设为false)。我们需要把整张图中每一个像素点位置作好记号,标记哪些点是需要绘制,哪些点是不需要绘制或者是已经绘制过。用一个boolean[][]型数组保存。还需要记录最后一次访问的点,以便继续下一次的绘制。根据最后一次访问的点继续寻找最近点,反复迭代,把所有的点绘制完成后,整张图就出来了。程序开始时,将最后一次访问的点初始化为左上角的点。

    
        private Point mLastPoint = new Point(0, 0);
    //获取下一个需要绘制的点
        private Point getNextPoint() {
            mLastPoint = getNearestPoint(mLastPoint);
            return mLastPoint;
        }
    

    接下来是将点绘制到Bitmap上,在将Bitmap绘制到SurfaceView的Canvas上。这里这么做的目的是,SurfaceView内部使用了双缓存,直接绘制到SurfaceView的Canvas可能会闪屏。

    /**
         * //绘制
         * return :false 表示绘制完成,true表示还需要继续绘制
         */
        private boolean draw() {
    
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setColor(Color.BLACK);
            //获取count个点后,一次性绘制到bitmap在把bitmap绘制到SurfaceView
            int count = 100;
            Point p = null;
            while (count-- > 0) {
                p = getNextPoint();
                if (p == null) {//如果p为空,说明所有的点已经绘制完成
                    return false;
                }
                mTmpCanvas.drawPoint(p.x, p.y + offsetY, mPaint);
            }
            //将bitmap绘制到SurfaceView中
            Canvas canvas = mSurfaceHolder.lockCanvas();
            canvas.drawBitmap(mTmpBm, 0, 0, mPaint);
            if (p != null)
                canvas.drawBitmap(mPaintBm, p.x, p.y - mPaintBm.getHeight() + offsetY, mPaint);
            mSurfaceHolder.unlockCanvasAndPost(canvas);
            return true;
        }

    基本上绘制算完成了,附上完整的代码:

    
    package com.hc.myoutline;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Point;
    import android.util.AttributeSet;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    
    /**
     * Package com.hc.myoutline
     * Created by HuaChao on 2016/5/27.
     */
    public class DrawOutlineView extends SurfaceView implements SurfaceHolder.Callback {
    
        private SurfaceHolder mSurfaceHolder;
        private Bitmap mTmpBm;
        private Canvas mTmpCanvas;
        private int mWidth;
        private int mHeight;
        private Paint mPaint;
        private int mSrcBmWidth;
        private int mSrcBmHeight;
        private boolean[][] mArray;
        private int offsetY = 100;
    
        private Bitmap mPaintBm;
        private Point mLastPoint = new Point(0, 0);
    
        public DrawOutlineView(Context context) {
            super(context);
            init();
        }
    
        public DrawOutlineView(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            init();
        }
    
        private void init() {
            mSurfaceHolder = getHolder();
            mSurfaceHolder.addCallback(this);
            mPaint = new Paint();
            mPaint.setColor(Color.BLACK);
        }
    
        //设置画笔图片
        public void setPaintBm(Bitmap paintBm) {
            mPaintBm = paintBm;
        }
    
        //获取离指定点最近的一个未绘制过的点
        private Point getNearestPoint(Point p) {
            if (p == null) return null;
            //以点p为中心,向外扩大搜索范围,每次搜索的是与p点相距add的正方形
            for (int add = 1; add < mSrcBmWidth && add < mSrcBmHeight; add++) {
                //
                int beginX = (p.x - add) >= 0 ? (p.x - add) : 0;
                int endX = (p.x + add) < mSrcBmWidth ? (p.x + add) : mSrcBmWidth - 1;
                int beginY = (p.y - add) >= 0 ? (p.y - add) : 0;
                int endY = (p.y + add) < mSrcBmHeight ? (p.y + add) : mSrcBmHeight - 1;
                //搜索正方形的上下边
                for (int x = beginX; x <= endX; x++) {
                    if (mArray[x][beginY]) {
                        mArray[x][beginY] = false;
                        return new Point(x, beginY);
                    }
                    if (mArray[x][endY]) {
                        mArray[x][endY] = false;
                        return new Point(x, endY);
                    }
                }
                //搜索正方形的左右边
                for (int y = beginY + 1; y <= endY - 1; y++) {
                    if (mArray[beginX][y]) {
                        mArray[beginX][beginY] = false;
                        return new Point(beginX, beginY);
                    }
                    if (mArray[endX][y]) {
    
                        mArray[endX][y] = false;
                        return new Point(endX, y);
                    }
                }
            }
    
            return null;
        }
    
        //获取下一个需要绘制的点
        private Point getNextPoint() {
            mLastPoint = getNearestPoint(mLastPoint);
            return mLastPoint;
        }
    
    
        /**
         * //绘制
         * return :false 表示绘制完成,true表示还需要继续绘制
         */
        private boolean draw() {
    
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setColor(Color.BLACK);
            //获取count个点后,一次性绘制到bitmap在把bitmap绘制到SurfaceView
            int count = 100;
            Point p = null;
            while (count-- > 0) {
                p = getNextPoint();
                if (p == null) {//如果p为空,说明所有的点已经绘制完成
                    return false;
                }
                mTmpCanvas.drawPoint(p.x, p.y + offsetY, mPaint);
            }
            //将bitmap绘制到SurfaceView中
            Canvas canvas = mSurfaceHolder.lockCanvas();
            canvas.drawBitmap(mTmpBm, 0, 0, mPaint);
            if (p != null)
                canvas.drawBitmap(mPaintBm, p.x, p.y - mPaintBm.getHeight() + offsetY, mPaint);
            mSurfaceHolder.unlockCanvasAndPost(canvas);
            return true;
        }
        //重画
        public void reDraw(boolean[][] array) {
            if (isDrawing) return;
            mTmpBm = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
            mTmpCanvas = new Canvas(mTmpBm);
            mPaint.setColor(Color.WHITE);
            mPaint.setStyle(Paint.Style.FILL);
            mTmpCanvas.drawRect(0, 0, mWidth, mHeight, mPaint);
            mLastPoint = new Point(0, 0);
            beginDraw(array);
        }
    
        private boolean isDrawing = false;
    
        public void beginDraw(boolean[][] array) {
            if (isDrawing) return;
            this.mArray = array;
            mSrcBmWidth = array.length;
            mSrcBmHeight = array[0].length;
    
            new Thread() {
                @Override
                public void run() {
                    while (true) {
                        isDrawing = true;
                        boolean rs = draw();
                        if (!rs) break;
                        try {
                            sleep(20);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    isDrawing = false;
                }
            }.start();
        }
    
    
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
    
        }
    
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            this.mWidth = width;
            this.mHeight = height;
            mTmpBm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            mTmpCanvas = new Canvas(mTmpBm);
            mPaint.setColor(Color.WHITE);
            mPaint.setStyle(Paint.Style.FILL);
            mTmpCanvas.drawRect(0, 0, mWidth, mHeight, mPaint);
            Canvas canvas = holder.lockCanvas();
            canvas.drawBitmap(mTmpBm, 0, 0, mPaint);
            holder.unlockCanvasAndPost(canvas);
    
            mPaint.setStyle(Paint.Style.STROKE);
        }
    
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
    
        }
    }
    

    接下来是在MainActivity里面传入参数过来,附上MainActivity的代码:

    package com.hc.myoutline;
    
    import android.graphics.Bitmap;
    import android.graphics.Color;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.view.MotionEvent;
    
    public class MainActivity extends AppCompatActivity {
        private DrawOutlineView drawOutlineView;
        private Bitmap sobelBm;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            setContentView(R.layout.activity_main);
            //将Bitmap压缩处理,防止OOM
            Bitmap bm = CommenUtils.getRatioBitmap(this, R.drawable.test, 100, 100);
            //返回的是处理过的Bitmap
            sobelBm = SobelUtils.Sobel(bm);
            drawOutlineView = (DrawOutlineView) findViewById(R.id.outline);
    
            Bitmap paintBm = CommenUtils.getRatioBitmap(this, R.drawable.paint, 10, 20);
            drawOutlineView.setPaintBm(paintBm);
    
        }
        //根据Bitmap信息,获取每个位置的像素点是否需要绘制
        //使用boolean数组而不是int[][]主要是考虑到内存的消耗
        private boolean[][] getArray(Bitmap bitmap) {
            boolean[][] b = new boolean[bitmap.getWidth()][bitmap.getHeight()];
    
            for (int i = 0; i < bitmap.getWidth(); i++) {
                for (int j = 0; j < bitmap.getHeight(); j++) {
                    if (bitmap.getPixel(i, j) != Color.WHITE)
                        b[i][j] = true;
                    else
                        b[i][j] = false;
                }
            }
            return b;
        }
    
        boolean first = true;
    
       //点击时开始绘制
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (first) {
                first = false;
                drawOutlineView.beginDraw(getArray(sobelBm));
            } else
                drawOutlineView.reDraw(getArray(sobelBm));
            return true;
        }
    }

    关于轮廓提取的具体实现代码这里不粘出,可以去GitHub查看:GraphicLib 或者是下载我的源代码查看。所有内容已经结束,赶紧下载源码运行一下你的照片去秀一下你的逼格吧!

    最后的最后:请注意,源码中,轮廓的提取是运行在主线程中,如果图片比较复杂,可能会导致ANR,建议另开线程处理。别问为什么我不去改,一个字:懒!另外,接下来一篇文章中,我将介绍提高运算速度相关内容,提升轮廓提取速度,敬请期待~

    源码地址:Android自动手绘,圆你儿时画家梦!

    展开全文
  • 动态规划常见类型总结

    千次阅读 多人点赞 2019-03-26 23:55:28
    严格来说,递推不属于动态规划问题,因为动态规划不仅递推过程,还要决策(即取最优),但广义的动态规划是可以包含递推的,递推是一类简单的、特殊的动态规划,毕竟动态规划与递推密不可分。动态规划类型主要...
  • WPF自定义Button控件实现圆角

    千次阅读 2018-03-28 16:32:29
    Button实现圆角很简单只需Button ContentTemplate修改样式模板就可以实现了。但是这样无法达到复用,比如另一个按钮设计的不需要圆角样式又用的同一套怎么办?当然可以在把样式复制下然后改一个样式名字修改下样式就...
  • 动态规划模板总结

    千次阅读 2018-08-06 17:06:47
    动态规划常常适用于重叠子问题和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。 试用情况: 最优子结构性质。如果问题的最优解所包含的子问题的解也是最优的,我们就称该问题具有最优子结构...
  • 区间动态规划详解

    千次阅读 2018-11-15 20:57:40
    前段时间遇到石子合并问题,看着题解A了,以为学会了区间DP,再次遇到能量项链这个问题的时候大脑还是一片空白,只能重新认识一下区间动态规划了。 翻过很多博客,基本上都是题解,真正对区间动态规划本身的讲解...
  • 渐变圆环进度条实现

    千次阅读 2017-09-25 20:59:24
    那开始绘制我们想要的效果之前,我们一定会给我们的弧形设两个基本值,那就是弧形起点的颜色,和结尾的颜色,所以中间的哪些颜色都是根据这两个颜色进行计算得来的,好了,拿到这两个值,我们可以开始计算每个位置的...
  • 动态寻路

    千次阅读 2015-07-08 14:27:59
    动态寻路 目标  在一个所有物体都在动态移动的场景中,调用物理引擎现有的能力让一个实体从任意一点走到另一点的同时能够逼真地躲开所有可能与此物相撞的物体。... 如果你要把一个AI(人工智能)元素的游
  • Cocos2d-x 实时动态阴影

    千次阅读 2019-02-17 22:21:58
    Cocos2d-x 实时动态阴影 声明:本文使用的是cocos2d-x-3.17的代码 本文特效需要用到以下知识 Cocos2d摄像机详解 Cocos2d-x 着色器 Cocos2d-x 3D模型渲染 在Cocos2d-x-3.17的CPP Tests测试例子中一个“假”...
  • 直线与的位置关系怎么判断

    千次阅读 2017-06-21 16:37:00
    《直线与的位置关系》这个知识点应用比较广泛,是几何知识的一个综合运用,在今后的解题及几何证明中,将起到重要的作用,所以就必须熟练掌握它们之间有哪些位置关系。为了更好地理解,我们可以借助数学绘图工具来...
  • Android 动态设置Shape

    千次阅读 2017-08-24 18:38:19
    引言:之前涉及到设置...于是就想用动态设置shape的方式来替换静态配置shape标签。静态配置shape这个不多说了,梯子备好了,自行前往! https://developer.android.google.cn/guide/topics/resources/drawable-resour
  • 这其中哪些文字是关键,如果你想到是通过canvas的drawText()函数绘制上去就要类似你了,而且这些椭圆也许不是一个定值,所以这个方法最好别用,哪到底用了什么方法把这些文字绘制上去的呢?请看代码 package ...
  • 最近同学私信问,他在CAD里画了一个圆形的法兰盘,希望在圆形的设备上打孔,根据需要,可能要均布4个或者6个圆孔。 他已经将圆孔的半径和圆孔所在的位置都用动态块来完成了,那么,能不能把这个改变均布数目的操作...
  • 方向分布(标准差椭圆)

    万次阅读 多人点赞 2017-01-05 15:53:27
    点模式的分析中,一般会考察如下五种内容:   1、点的疏密,包括点数据的分布...5、其他,如点的一些动态变化等。 (关于点数据分析的其他详细的内容,请看虾神以前的文章,或者再公众号里面回复“点分析”)  
  • 静态库,动态库,静态链接和动态链接这几个概念理解起来很简单,本文主要是收集了网上的一些观点作了一个较全面的总结。 一、问题 1,静态库是什么? 2,动态库是什么? 3,两者的区别? 4,静态链接? 5...
  • Android动态设置Shape

    千次阅读 2016-11-22 18:58:50
    动态的改变控件的颜色
  • Alimama最近推出了圆角形式...所以这里每块不同颜色的区域都是一个div,需要根据不同参数用JavaScript动态生成div。这里以右上方的小圆角为例简单分析下几个实现要点,呵呵,完整代码就算佬,重点是相互学习么。其实实
  • 近三年前的一篇旧文,虽然在浏览器和相关技术标准方面过时之处,但总体上对概念的理解、开发的思路和代码样例仍然参考价值。背景当Javascript被Netscape公司发明出来时,它被用来做一些琐细的事情,比如校验表单...
  • 动态链接库

    千次阅读 2008-04-07 23:07:00
    一、动态链接库的概念 动态链接库(Dynamic Link Library...动态链接库文件的扩展名一般是dll,也可能是drv、sys和fon,它和可执行文件(exe)非常类似,区别在于DLL中虽然包含了可执行代码却不能单独执行,而应由Win
  • UML动态模型图简单介绍

    千次阅读 2015-03-17 20:06:57
    UML动态模型图描述了系统动态行为的各个方面,包括用例图、序列图、协作图、活动图和状态图。下面就每种图做一个简单介绍: 用例图 用例图描述系统外部的执行者与系统提供的用例之间的某种联系。所谓用例是指对...
  • GradientDrawable 动态设置背景的使用

    千次阅读 2017-03-08 14:32:42
    我们经常会为控件按钮等设置背景图...当我们在代码中需要动态配置的时候,我们可以选用GradientDrawable这个类来实现我们的效果。支持的形状4种: /** * Shape is a rectangle, possibly with rounded corners */矩
  • HTML 5 动态效果制作方法整理

    千次阅读 2016-11-28 14:14:40
    0.备注如果发现某些图片未正常加载,可以点击下面链接,查看文章。HTML 5 动态效果制作方法...2.动态效果的分类我们首先先来简单看一下,动态效果的制作有哪些分类?3.GIF首先第一个就是我们的 gif 图片,这是一种
  • Canvas三种动态画圆实现方法说明

    千次阅读 2018-12-17 09:33:16
    canvas是HTML5出来的绘图API容器,对于图形的处理非常强大,下面使用canvas配合JavaScript来做一下动态画圆效果。可以用它来做圆形进度条来使用。 以下是三种实现方法: 方法一:arc()实现画圆 通过设置的开始角度和...
  • 从上图,我们基本上就可以看出方向分布工具的主要作用了,它可以识别一组数据的方向以及分布的趋势,并且了解到这份数据是否具有一些特性,至于有哪些特性,我们后面再说。   我们先来看看这个标准差椭圆的...
  • 下面来看看它有哪些基本属性: app:cardBackgroundColor这是设置背景颜色  app:cardCornerRadius这是设置圆角大小  app:cardElevation这是设置z轴的阴影  app:cardMaxElevation这是设置z轴的最大高度...
  • 很多时候我们想要自己写一些类似时钟、罗盘的控件,却又找不到...在AndroidCanvas和Paint这么好的画师的情况下,还是选择使用图片,的确是一些尴尬了。下面我就利用一步一步实现自定义时钟来对这个问题做一个讲解。
  • VC++动态连接库详解

    千次阅读 2017-01-18 17:52:37
    1.概论   先来阐述一下DLL(Dynamic Linkable ...静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib中的指令都被直接包含在最终生成的EXE文件中了。但是若使用DLL,该

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 30,313
精华内容 12,125
关键字:

动态的圆有哪些