详解Android自定义View--自定义柱状图

绪论

转眼间,2016伴随着互联网寒冬和帝都的雾霾马上就过去了,不知道大家今年一整年过得怎么样?最近票圈被各个城市的雾霾刷屏,内心难免会动荡,庆幸自己早出来一年,也担忧着自己的未来的职业规划。无所谓了,既然选择了这个行业,我觉得大家就应该坚持下去,路是自己走的,及时再寒冬,只要你足够优秀,足够努力,相信你最后还是会找到自己满意的工作的。最后还要感谢今年博客之星大家对我的投票支持,非常感谢。不多说了,今天的主题是它–对,自定义View柱状图。

先来说说我最近在做什么吧?好久没有写博客了,最近手里有两个项目,闲的时候一直在忙着做项目,也封装了属于自己的一套Library,抽下来我会把它分享出来的。公司的项目也一直在忙,今天的柱状图就是公司的项目所用到的。先来看一下效果吧

具体实现

可以看到,今天的柱状图分为三类:双条竖向柱状图、单条竖向柱状图以及单条横向柱状图,其实原理都是一样的,下面我们具体看一下怎么实现,怎么去画一个这样的柱状图。

双条竖向

我们可以看到这个柱状图主要包括下面几个方面:

  • 双条柱状图
  • 横坐标月份
  • 点击tips显示具体数值
  • 灰色阴影(图上没有显示具体看代码)
  • 柱状图渐变、圆角、点击变色

好了上面五点就是需求和UI所提出来的所有东西,我们开始着手去“画”吧。

1.首先我们定义一些资源style供使用

包括

  • leftColor 左侧柱状图顶部颜色
  • leftColorBottom 左侧柱状图底部颜色
  • rightColor 右侧柱状图顶部颜色
  • rightColorBottom 右侧柱状图底部颜色
  • selectRightColor 左侧点击选中颜色
  • selectRightColor 右侧点击选中颜色
  • xyColor 横轴字体颜色

底部和顶部颜色是用于渐变用的

<declare-styleable name="MyChartView">
    <attr name="leftColor" format="color"></attr>
    <attr name="leftColorBottom" format="color"></attr>
    <attr name="selectLeftColor" format="color"></attr>
    <attr name="rightColor" format="color"></attr>
    <attr name="rightColorBottom" format="color"></attr>
    <attr name="selectRightColor" format="color"></attr>
    <attr name="xyColor" format="color"></attr>
  </declare-styleable>

2.接下来我们看具体代码,注释写的很详细了,仔细看:

  • 初始化属性、画笔、所用的size等
  • 测量计算高宽度等
  • 画坐标轴、画月份、画柱状图、画阴影
  • 柱状图渐变以及点击变色
  • touch点击事件判断点击所属哪个月份,接口回调给activity显示具体月份数值

注意:onWindowVisibilityChanged这个方法(当屏幕焦点变化时重新侧向起始位置,必须重写次方法,否则当焦点变化时柱状图会跑到屏幕外面)

下面主要说一下绘制部分吧

OnDraw()部分

我们将每次onTouch的条的索引放到selectIndexRoles数组中,然后当这个数组包含该绘制的柱状图的索引是我们设置不用颜色以及不设置渐变;

同时我们给每两个双条之间的的空白处绘制成阴影;

最后drawRoundRect()就绘制了一个圆角的矩形。

//画柱状图
    for (int i = 0; i < list.size(); i++) {
      int size = mHeight / 120;
      if (selectIndexRoles.contains(i)) {
        //偶数
        mChartPaint.setShader(null);
        if (i % 2 == 0) {
          mChartPaint.setColor(selectLeftColor);
        } else {
          mChartPaint.setColor(selectRightColor);
        }
      } else {
        //偶数
        if (i % 2 == 0) {
          LinearGradient lg = new LinearGradient(mChartWidth, mChartWidth + mSize, mHeight - 100,
            (float) (mHeight - 100 - list.get(i) * size), lefrColorBottom, leftColor, Shader.TileMode.MIRROR);
          mChartPaint.setShader(lg);
        } else {
          LinearGradient lg = new LinearGradient(mChartWidth, mChartWidth + mSize, mHeight - 100,
            (float) (mHeight - 100 - list.get(i) * size), rightColorBottom, rightColor, Shader.TileMode.MIRROR);
          mChartPaint.setShader(lg);
        }
      }

      mChartPaint.setStyle(Paint.Style.FILL);
      //画阴影
      if (i == number * 2 || i == number * 2 + 1) {
        mShadowPaint.setColor(Color.BLUE);
      } else {
        mShadowPaint.setColor(Color.WHITE);
      }

      //画柱状图
      RectF rectF = new RectF();
      rectF.left = mChartWidth;
      rectF.right = mChartWidth + mSize;
      rectF.bottom = mHeight - 100;
      rectF.top = (float) (mHeight - 100 - list.get(i) * size);
      canvas.drawRoundRect(rectF, 10, 10, mChartPaint);
      //canvas.drawRect(mChartWidth, mHeight - 100 - list.get(i) * size, mChartWidth + mSize, mHeight - 100, mChartPaint)
      // ;// 长方形
      mChartWidth += (i % 2 == 0) ? (3 + getWidth() / 39) : (getWidth() / 13 - 3 - mSize);
    }

全部代码

package com.hankkin.mycartdemo.chatview;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import com.hankkin.mycartdemo.R;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Hankkin on 2016/12/10.
 */

public class MyChartView extends View {

  private int leftColor;//双柱左侧
  private int rightColor;//双柱右侧
  private int lineColor;//横轴线
  private int selectLeftColor;//点击选中左侧
  private int selectRightColor;//点击选中右侧
  private int lefrColorBottom;//左侧底部
  private int rightColorBottom;//右侧底部
  private Paint mPaint, mChartPaint, mShadowPaint;//横轴画笔、柱状图画笔、阴影画笔
  private int mWidth, mHeight, mStartWidth, mChartWidth, mSize;//屏幕宽度高度、柱状图起始位置、柱状图宽度
  private Rect mBound;
  private List<Float> list = new ArrayList<>();//柱状图高度占比
  private Rect rect;//柱状图矩形
  private getNumberListener listener;//点击接口
  private int number = 1000;//柱状图最大值
  private int selectIndex = -1;//点击选中柱状图索引
  private List<Integer> selectIndexRoles = new ArrayList<>();

  public void setList(List<Float> list) {
    this.list = list;
    mSize = getWidth() / 39;
    mStartWidth = getWidth() / 13;
    mChartWidth = getWidth() / 13 - mSize - 3;
    invalidate();
  }

  public void setListener(getNumberListener listener) {
    this.listener = listener;
  }

  public MyChartView(Context context) {
    this(context, null);
  }

  public MyChartView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public MyChartView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    //获取我们自定义的样式属性
    TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyChartView, defStyleAttr, 0);
    int n = array.getIndexCount();
    for (int i = 0; i < n; i++) {
      int attr = array.getIndex(i);
      switch (attr) {
        case R.styleable.MyChartView_leftColor:
          // 默认颜色设置为黑色
          leftColor = array.getColor(attr, Color.BLACK);
          break;
        case R.styleable.MyChartView_selectLeftColor:
          // 默认颜色设置为黑色
          selectLeftColor = array.getColor(attr, Color.BLACK);
          break;
        case R.styleable.MyChartView_rightColor:
          rightColor = array.getColor(attr, Color.BLACK);
          break;
        case R.styleable.MyChartView_selectRightColor:
          selectRightColor = array.getColor(attr, Color.BLACK);
          break;
        case R.styleable.MyChartView_xyColor:
          lineColor = array.getColor(attr, Color.BLACK);
          break;
        case R.styleable.MyChartView_leftColorBottom:
          lefrColorBottom = array.getColor(attr, Color.BLACK);
          break;
        case R.styleable.MyChartView_rightColorBottom:
          rightColorBottom = array.getColor(attr, Color.BLACK);
          break;
      }
    }
    array.recycle();
    init();
  }

  //初始化画笔
  private void init() {
    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    mBound = new Rect();
    mChartPaint = new Paint();
    mChartPaint.setAntiAlias(true);
    mShadowPaint = new Paint();
    mShadowPaint.setAntiAlias(true);
    mShadowPaint.setColor(Color.WHITE);
  }

  //测量高宽度
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int width;
    int height;
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);

    if (widthMode == MeasureSpec.EXACTLY) {
      width = widthSize;
    } else {
      width = widthSize * 1 / 2;
    }
    if (heightMode == MeasureSpec.EXACTLY) {
      height = heightSize;
    } else {
      height = heightSize * 1 / 2;
    }

    setMeasuredDimension(width, height);
  }

  //计算高度宽度
  @Override
  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    mWidth = getWidth();
    mHeight = getHeight();
    mStartWidth = getWidth() / 13;
    mSize = getWidth() / 39;
    mChartWidth = getWidth() / 13 - mSize;
  }

  //重写onDraw绘制
  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    mPaint.setColor(lineColor);
    //画坐标轴
    //canvas.drawLine(0, mHeight - 100, mWidth, mHeight - 100, mPaint);
    for (int i = 0; i < 12; i++) {
      //画刻度线
      //canvas.drawLine(mStartWidth, mHeight - 100, mStartWidth, mHeight - 80, mPaint);
      //画数字
      mPaint.setTextSize(35);
      mPaint.setTextAlign(Paint.Align.CENTER);
      mPaint.getTextBounds(String.valueOf(i + 1) + "", 0, String.valueOf(i).length(), mBound);
      canvas.drawText(String.valueOf(i + 1) + "月", mStartWidth - mBound.width() * 1 / 2,
        mHeight - 60 + mBound.height() * 1 / 2, mPaint);
      mStartWidth += getWidth() / 13;
    }
    //画柱状图
    for (int i = 0; i < list.size(); i++) {
      int size = mHeight / 120;
      if (selectIndexRoles.contains(i)) {
        //偶数
        mChartPaint.setShader(null);
        if (i % 2 == 0) {
          mChartPaint.setColor(selectLeftColor);
        } else {
          mChartPaint.setColor(selectRightColor);
        }
      } else {
        //偶数
        if (i % 2 == 0) {
          LinearGradient lg = new LinearGradient(mChartWidth, mChartWidth + mSize, mHeight - 100,
            (float) (mHeight - 100 - list.get(i) * size), lefrColorBottom, leftColor, Shader.TileMode.MIRROR);
          mChartPaint.setShader(lg);
        } else {
          LinearGradient lg = new LinearGradient(mChartWidth, mChartWidth + mSize, mHeight - 100,
            (float) (mHeight - 100 - list.get(i) * size), rightColorBottom, rightColor, Shader.TileMode.MIRROR);
          mChartPaint.setShader(lg);
        }
      }

      mChartPaint.setStyle(Paint.Style.FILL);
      //画阴影
      if (i == number * 2 || i == number * 2 + 1) {
        mShadowPaint.setColor(Color.BLUE);
      } else {
        mShadowPaint.setColor(Color.WHITE);
      }

      //画柱状图
      RectF rectF = new RectF();
      rectF.left = mChartWidth;
      rectF.right = mChartWidth + mSize;
      rectF.bottom = mHeight - 100;
      rectF.top = (float) (mHeight - 100 - list.get(i) * size);
      canvas.drawRoundRect(rectF, 10, 10, mChartPaint);
      //canvas.drawRect(mChartWidth, mHeight - 100 - list.get(i) * size, mChartWidth + mSize, mHeight - 100, mChartPaint)
      // ;// 长方形
      mChartWidth += (i % 2 == 0) ? (3 + getWidth() / 39) : (getWidth() / 13 - 3 - mSize);
    }
  }

  @Override
  public void onWindowFocusChanged(boolean hasWindowFocus) {
    super.onWindowFocusChanged(hasWindowFocus);
    if (hasWindowFocus) {

    }
  }

  /**
   * 注意:
   * 当屏幕焦点变化时重新侧向起始位置,必须重写次方法,否则当焦点变化时柱状图会跑到屏幕外面
   */

  @Override
  protected void onWindowVisibilityChanged(int visibility) {
    super.onWindowVisibilityChanged(visibility);
    if (visibility == VISIBLE) {
      mSize = getWidth() / 39;
      mStartWidth = getWidth() / 13;
      mChartWidth = getWidth() / 13 - mSize - 3;
    }
  }

  /**
   * 柱状图touch事件
   * 获取触摸位置计算属于哪个月份的
   * @param ev
   * @return
   */
  @Override
  public boolean onTouchEvent(MotionEvent ev) {

    int x = (int) ev.getX();
    int y = (int) ev.getY();
    int left = 0;
    int top = 0;
    int right = mWidth / 12;
    int bottom = mHeight - 100;
    switch (ev.getAction()) {
      case MotionEvent.ACTION_DOWN:
        for (int i = 0; i < 12; i++) {
          rect = new Rect(left, top, right, bottom);
          left += mWidth / 12;
          right += mWidth / 12;
          if (rect.contains(x, y)) {
            listener.getNumber(i, x, y);
            number = i;
            selectIndex = i;
            selectIndexRoles.clear();
            ;
            selectIndexRoles.add(selectIndex * 2 + 1);
            selectIndexRoles.add(selectIndex * 2);
            invalidate();
          }
        }
        break;
      case MotionEvent.ACTION_UP:

        break;
    }
    return true;
  }

  public interface getNumberListener {
    void getNumber(int number, int x, int y);
  }

  public int getLeftColor() {
    return leftColor;
  }

  public void setLeftColor(int leftColor) {
    this.leftColor = leftColor;
  }

  public int getRightColor() {
    return rightColor;
  }

  public void setRightColor(int rightColor) {
    this.rightColor = rightColor;
  }

  public int getLineColor() {
    return lineColor;
  }

  public void setLineColor(int lineColor) {
    this.lineColor = lineColor;
  }

  public int getSelectLeftColor() {
    return selectLeftColor;
  }

  public void setSelectLeftColor(int selectLeftColor) {
    this.selectLeftColor = selectLeftColor;
  }

  public int getSelectRightColor() {
    return selectRightColor;
  }

  public void setSelectRightColor(int selectRightColor) {
    this.selectRightColor = selectRightColor;
  }

  public int getLefrColorBottom() {
    return lefrColorBottom;
  }

  public void setLefrColorBottom(int lefrColorBottom) {
    this.lefrColorBottom = lefrColorBottom;
  }

  public int getRightColorBottom() {
    return rightColorBottom;
  }

  public void setRightColorBottom(int rightColorBottom) {
    this.rightColorBottom = rightColorBottom;
  }
}

3.具体使用:

private void initChatView() {

    myChartView.setLefrColorBottom(getResources().getColor(R.color.leftColorBottom));
    myChartView.setLeftColor(getResources().getColor(R.color.leftColor));
    myChartView.setRightColor(getResources().getColor(R.color.rightColor));
    myChartView.setRightColorBottom(getResources().getColor(R.color.rightBottomColor));
    myChartView.setSelectLeftColor(getResources().getColor(R.color.selectLeftColor));
    myChartView.setSelectRightColor(getResources().getColor(R.color.selectRightColor));
    myChartView.setLineColor(getResources().getColor(R.color.xyColor));
    chartList = new ArrayList<>();

    relativeLayout = (RelativeLayout) findViewById(R.id.linearLayout);
    relativeLayout.removeView(llChart);
    Random random = new Random();
    while (chartList.size() < 24) {
      int randomInt = random.nextInt(100);
      chartList.add((float) randomInt);
    }
    myChartView.setList(chartList);
    myChartView.setListener(new MyChartView.getNumberListener() {
      @Override
      public void getNumber(int number, int x, int y) {
        relativeLayout.removeView(llChart);
        //反射加载点击柱状图弹出布局
        llChart = (LinearLayout) LayoutInflater.from(MainActivity.this).inflate(R.layout.layout_shouru_zhichu, null);
        TextView tvZhichu = (TextView) llChart.findViewById(R.id.tv_zhichu);
        TextView tvShouru = (TextView) llChart.findViewById(R.id.tv_shouru);
        tvZhichu.setText((number + 1) + "月支出" + " " + chartList.get(number * 2));
        tvShouru.setText ( "收入: " + chartList.get(number * 2 + 1));
        llChart.measure(0, 0);//调用该方法后才能获取到布局的宽度
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
          RelativeLayout.LayoutParams.WRAP_CONTENT);
        params.leftMargin = x - 100;
        if (x - 100 < 0) {
          params.leftMargin = 0;
        } else if (x - 100 > relativeLayout.getWidth() - llChart.getMeasuredWidth()) {
          //设置布局距左侧屏幕宽度减去布局宽度
          params.leftMargin = relativeLayout.getWidth() - llChart.getMeasuredWidth();
        }
        llChart.setLayoutParams(params);
        relativeLayout.addView(llChart);
      }
    });
  }

经过以上步骤,我们的双条竖向柱状图就绘制完成了,也显示出来了。其实自己坐下来仔细拿笔算一下,画一下,也没有想象的那么难。至于单条和横向的实现原理都一样,比这个要简单的多。哦对了,横向的我只是自定义了一个横向的柱状图View,然后用ListView显示的各个部门的具体内容。

代码下载:demo

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android自定义带增长动画和点击弹窗提示效果的柱状图DEMO

    项目中最近用到各种图表,本来打算用第三方的,例如MPAndroid,这是一个十分强大的图表库,应用起来十分方便,但是最终发现和设计不太一样,没办法,只能自己写了.今天将写好的柱状图的demo贴在这,该柱状图可根据数据的功能有一下几点: 1. 根据数据的多少,动态的绘制柱状图柱子的条数: 2. 柱状图每条柱子的绘制都有动态的动画效果: 3. 每条柱子有点击事件,点击时弹出提示框,显示相关信息,规定时间后,弹窗自动消失. 好了,先上演示图: 下边贴出相关代码: 自定义柱状图类: package co

  • 100行Android代码轻松实现带动画柱状图

    为何要用带动画的柱状图呢? 最近,项目中遇到一个地方,要用到柱状图.所以这篇文章主要讲怎么搞一个柱子.100行代码,搞定柱状图! 圆角,头顶带数字.恩,这样用drawable也可以搞定.但是,这个柱子是有一个动画的,就是进入到界面的时候柱子不断的长高.这样的话,综合考虑还是用自定义View来做比较简便.效果如下图了: 完整Demo地址请到我的github下载地址: https://github.com/lixiaodaoaaa/ColumnAnimViewProject 关于尺寸 控件尺寸直接来

  • Android自定义view实现动态柱状图

    先看一下动态柱状图效果. 主要功能是可以自定义指定的字体,柱状图颜色,动态效果. 在自定义view public class Histogram extends View { int MAX = 100;//矩形显示的最大值 int corner = 0; //矩形的角度. 设置为0 则没有角度. double data = 0.0;//显示的数 double tempData = 0; //初始数据 int textPadding = 50; //字体与矩形图的距离 Paint mPaint;

  • MPAndroidChart开源图表库的使用介绍之饼状图、折线图和柱状图

    MPAndroidChart开源图表库之饼状图 为大家介绍一款图标开源库MPAndroidChart,它不仅可以在Android设备上绘制各种统计图表,而且可以对图表进行拖动和缩放操作,用起来非常灵活.MPAndroidChart同样拥有常用的图表类型:线型图.饼图.柱状图和散点图. mpandroidchartlibrary.jar包下载地址: https://github.com/PhilJay/MPAndroidChart/releases 下面主要实现以下饼状图: 1.从上面的地址中下载

  • 详解Android如何实现自定义的动画曲线

    目录 前言 Curve 类定义 实例解析 正弦动画曲线 总结 前言 最近在写动画相关的篇章,经常会用到 Curve 这个动画曲线类,那这个类到底怎么实现的?如果想自己来一个自定义的动画曲线该怎么弄?本篇我们就来一探究竟. 曲线 Curve 类定义 查看源码, Curve 类定义如下: abstract class Curve extends ParametricCurve<double> {   const Curve();   @override   double transform(dou

  • 详解Android Material Design自定义动画的编写

    新的动画Api,让你在UI控件里能创建触摸反馈,改变View的状态,切换activity的一系列自定义动画 具体有: 响应View的touch事件的触摸反馈动画 隐藏和显示View的循环展示动画 两个Activity间的切换动画 更自然的曲线运动的动画 使用View的状态更改动画,能改变一个或多个View的属性 在View的状态更改时显示状态列表动画 这些new animations Api,已内置在标准Widget中,如Button.在自定义view时也可使用这些api 动画在Material

  • 源码详解Android中View.post()用法

    emmm,大伙都知道,子线程是不能进行 UI 操作的,或者很多场景下,一些操作需要延迟执行,这些都可以通过 Handler 来解决.但说实话,实在是太懒了,总感觉写 Handler 太麻烦了,一不小心又很容易写出内存泄漏的代码来,所以为了偷懒,我就经常用 View.post() or View.postDelay() 来代替 Handler 使用. 但用多了,总有点心虚,View.post() 会不会有什么隐藏的问题?所以趁有点空余时间,这段时间就来梳理一下,View.post() 原理到底是什

  • 详解Android如何自定义view实现圆形进度条

    Android中实现进度条有很多种方式,自定义进度条一般是继承progressBar或继承view来实现,本篇中讲解的是第二种方式. 先上效果图: 实现圆形进度条总体来说并不难,还是跟往常一样继承view,初始化画笔,按下面的步骤一步步来就好了.对初学者来说动画效果可能比较陌生,我们可以使用属性动画中的valueAnimator来实现动画效果. 实现步骤: 1.画出一个灰色的圆环作为背景. 2.画出上层的圆环覆盖下方的圆环. 3.加入动画效果 值得注意的是怎么设置圆环和文字的位置. 画出矩形只需

  • 详解 Android中Libgdx使用ShapeRenderer自定义Actor解决无法接收到Touch事件的问题

    详解 Android中Libgdx使用ShapeRenderer自定义Actor解决无法接收到Touch事件的问题 今天在项目中实现了一个效果,主要是画一个圆.为了后续使用方便,将这个圆封装在一个自定义Actor(CircleActot)中,后续想显示一个圆的时候,只要创建一个CircleActor中即可. 部分代码如下所示: package com.ef.smallstar.unitmap.widget; import android.content.res.Resources; import

  • 详解Flutter如何完全自定义TabBar

    目录 前言 实现过程 完整代码 总结 前言 在App中TabBar形式交互是非常常见的,但是系统提供的的样式大多数又不能满足我们产品和UI的想法,这篇就记录下在Flutter中我在实现自定义TabBar的一个思路和过程,希望对你也有所帮助~ 先看下我最终的效果图: 实现过程 首先我们先看下TabBar的构造方法: const TabBar({ Key? key, required this.tabs,// tab组件列表 this.controller,// tabBar控制器 this.isS

  • 详解Vue中的自定义指令

    除了默认设置的核心指令( v-model 和 v-show ),Vue 也允许注册自定义指令.在Vue里,代码复用的主要形式和抽象是组件.然而,有的情况下,仍然需要对纯 DOM 元素进行底层操作,这时候就会用到自定义指令.本文将详细介绍Vue自定义指令 指令注册 以一个input元素自动获得焦点为例,当页面加载时,使用autofocus可以让元素将获得焦点 .但是autofocus在移动版Safari上不工作.现在注册一个使元素自动获取焦点的指令 指令注册类似于组件注册,包括全局指令和局部指令两

  • 详解Android 裸眼3D效果View控件

    描述:这是一个裸眼3D效果的控件View. Tips:本项目代码部分逻辑参考于其他文章(自如的3D裸眼实现),众人拾柴火焰高,希望大家能多多补充. 项目代码:https://gitee.com/jiugeishere/uidesign 控件效果如下: 实现功能: 实现三层图片叠加效果(裸眼3D效果) 可设置每层图片移动速率 可设置每层图片移动的限制度数 可直接设置图片或引入图片 设计核心: 主要的设计核心是依赖于传感器对手机晃动的监听(重力感应监听器),对每层图片进行不同的移动,实现仿3D效果.

  • 详解Python中的自定义密码验证

    目录 在测试:nut_and_bolt:️之前 试验contains_character TestContainsCharacter字符 试验is_valid_size TestIsValidSize 试验is_valid_password TestIsValidPassword 重构is_valid_password 结论 这些帖子将分为三个部分. 1.密码验证功能 2.重构密码验证函数 3.对密码验证功能进行单元测试 这是Python系列中自定义密码验证的第三部分,也是最后一部分.我们将看看

  • 详解QListWidget如何实现自定义Item效果

    首先,我们来看以下实现的最终效果吧! 我觉得这并不是一个很难得问题,最近新招了一个应届生,发现在实现上述效果时,被困扰住了,是不是刚刚接触Qt的这种稍微有难度的界面时,都会有些无头绪呢? 所以,我打算分享给大家实现的思路,以及会出现的问题,就我一个开发5年C++的员工而言,针对新手会遇到哪些不懂的问题. 当前的开发环境:win10 VS2017 + Qt5.14.2 x64 在实现过程中新手会出现的难点,如下 1:如何在QListWidget中添加带有按钮.文本等其它控件的一条数据? 2:选中每

随机推荐