Android自定义控件:进度条的四种实现方式(Progress Wheel的解析)

摘要:
最近一直在学习自定义控件,搜了许多大牛们Blog里分享的小教程,也上GitHub找了一些类似的控件进行学习。这种方法实现可以通过创建一个animation-list的XML文件,然后给系统API提供的ProgressBar的indeterminateDrawable属性就可以了。

最近一直在学习自定义控件,搜了许多大牛们Blog里分享的小教程,也上GitHub找了一些类似的控件进行学习。发现读起来都不太好懂,就想写这么一篇东西作为学习笔记吧。

一、控件介绍:

进度条在App中非常常见,例如下载进度、加载图片、打开文章、打开网页等等……都需要这么一个效果让用户知道我们的App正在读取,以构造良好 的交互。如果没有这样一个效果的话,用户没法知道东西有没有下载好、图片加载了没有、文章打开了没……会让用户很不爽。基于这样的情景我们的UI设计师们 创造了这样一个控件。

二、这篇文章会涉及的知识点:

跟我一样刚入门的Android菜鸟们,我推荐大家先了解一下这些知识点再往下看。这些知识点我也会推荐一些博客给大家看看,更推荐大家看文档里的解释,当然大牛们可以直接无视……

1、ClipDrawable类:能够对一个drawable类进行剪切操作(即只显示某一部分的区域,另一部分隐藏),显示多大的区域由level控制(level取值是0~10000)

【博客:http://blog.csdn.net/lonelyroamer/article/details/8244777】、没文档的可以在这看【http://www.apihome.cn/api/android/ClipDrawable.html】

2、自定义View:guolin大神的深入学习View四部曲

Android LayoutInflater原理分析,带你一步步深入了解View —— http://blog.csdn.net/guolin_blog/article/details/12921889】

Android视图绘制流程完全解析,带你一步步深入了解View —— http://blog.csdn.net/guolin_blog/article/details/16330267】

Android视图状态及重绘流程分析,带你一步步深入了解View —— http://blog.csdn.net/guolin_blog/article/details/17045157】

Android自定义View的实现方法,带你一步步深入了解View ——

http://blog.csdn.net/guolin_blog/article/details/17357967】

3、没看过我写的:Android自定义控件——老版优酷三级菜单的话,或许需要看看这个:

【RotateAnimation详解——】

三、Android上的实现方式:

(前三种方法比较简单,第四种方法是GitHub项目的解析,对前三种没兴趣可以直接跳到后边……)

1、效果图: Android自定义控件:进度条的四种实现方式(Progress Wheel的解析)第1张

将进度条的变换过程分解为一帧一帧的图片,将这些一帧一帧的图片连起来构成一个动画。常用于:手机阅读网页、逛社区时,加载图片、文章等不需要清楚知道加载进度,但是需要知道是否进行加载的情景。

这种方法实现可以通过创建一个animation-list的XML文件,然后给系统API提供的ProgressBar的indeterminateDrawable属性就可以了。(这个属性应该是类似于设置一个动画吧……)

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false" >
<item android:duration="150" >
<clip 
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_01"
android:gravity="left"/>
</item>
<item android:duration="150" >
<clip 
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_02"
android:gravity="left"/>
</item>
<item android:duration="150" >
<clip 
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_03"
android:gravity="left"/>
</item>
<item android:duration="150" >
<clip 
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_04"
android:gravity="left"/>
</item>
<item android:duration="150" >
<clip 
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_05"
android:gravity="left"/>
</item>
<item android:duration="150" >
<clip 
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_06"
android:gravity="left"/>
</item>
<item android:duration="150" >
<clip 
android:clipOrientation="horizontal"
android:drawable="@drawable/loading_07"
android:gravity="left"/>
</item>
<item android:duration="150" >
    <clip 
      android:clipOrientation="horizontal"
      android:drawable="@drawable/loading_08"
      android:gravity="left"/>
  </item>
  <item android:duration="150" >
    <clip 
      android:clipOrientation="horizontal"
      android:drawable="@drawable/loading_09"
      android:gravity="left"/>
  </item>
  <item android:duration="150" >
    <clip 
      android:clipOrientation="horizontal"
      android:drawable="@drawable/loading_10"
      android:gravity="left"/>
  </item>
  <item android:duration="150" >
    <clip 
      android:clipOrientation="horizontal"
      android:drawable="@drawable/loading_11"
      android:gravity="left"/>
  </item>
  <item android:duration="150" >
    <clip 
      android:clipOrientation="horizontal"
      android:drawable="@drawable/loading_12"
      android:gravity="left"/>
  </item>

</animation-list>
<ProgressBar 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:indeterminateDrawable="@drawable/progressbar1"
        />

2 、效果图: Android自定义控件:进度条的四种实现方式(Progress Wheel的解析)第2张

在上一篇有关自定义控件的博客里我们使用了一个RotateAnimation类来实现旋转效果 (http://blog.csdn.net/u012403246/article/details/41309161),其实,我们在这里也可以把一 张图片,通过旋转,达到我们要的效果。本质上和上一种方法没多大区别。

我们只需要创建一个rotate的XML,对其属性进行一些简单的设置,然后加入我们要用的图片就可以了。

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:pivotX="50%"
android:pivotY="50%"
android:fromDegrees="0"
android:toDegrees="360"
android:interpolator="@android:anim/accelerate_decelerate_interpolator" >
<bitmap 
android:antialias="true"
android:filter="true"
android:src="@drawable/loading_360"/>

</rotate>
<ProgressBar 
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminateDrawable="@drawable/progressbar2"/>

3、效果图: Android自定义控件:进度条的四种实现方式(Progress Wheel的解析)第3张

我们可以弄两张照片,第一张是纯黑色的,然后把这张照片中心挖一个圆出来,圆区域弄成白色,挖出来的圆弄成第二张照片。我们不妨叠加显示两张照片,刚开始把第二张完全“遮住”,随着加载进度的增加,我们减少遮住的区域把第二张照片慢慢的显示出来。

Android 上刚好就有这么一个ClipDrawable类,能够实现剪裁的过程。我们来看看怎么通过这样的方式自定义一个进度条控件。

public class MyProgressBar extends FrameLayout{
private boolean running;
private int progress = 0;
private static final int MAX_PROGRESS = 10000;
private ClipDrawable clip;
private Handler handler = new Handler(){
@Override
public void handleMessage(android.os.Message msg) {
if(msg.what == 0x123)
clip.setLevel(progress);
}
};
public MyProgressBar(Context context){
this(context,null,0);
}

public MyProgressBar(Context context,AttributeSet attrs){
this(context,null,0);
}
public MyProgressBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
Init(context);
}
public void Init(Context context){
View view = LayoutInflater.from(context).inflate(R.layout.view, null);
ImageView iv = (ImageView)view.findViewById(R.id.progress_img);
addView(view);
clip = (ClipDrawable)iv.getDrawable();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
running = true;
while(running){
handler.sendEmptyMessage(0x123);
if(progress == MAX_PROGRESS)
progress = 0;
progress += 100;
try {
Thread.sleep(18);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread.start();
}

public void stop(){
progress = 0;
running = false;
}
}

通过代码我们可以看到,逻辑非常简单,关键就在于ClipDrawable的setLevel()方法,这个是设置剪裁效果的。

4、效果图: Android自定义控件:进度条的四种实现方式(Progress Wheel的解析)第4张

实现一个View的子类——Progress Wheel类,实现进度条效果。具体的内容我都写在了注释上,如果不了解自定义控件的知识,可以去阅读guolin博客里自定义View四部曲的讲解,讲的挺好的。

代码 :

public class ProgressWheel extends View {

//绘制View用到的各种长、宽带大小
private int layout_height = 0;
private int layout_width = 0;
private int fullRadius = 100;
private int circleRadius = 80;
private int barLength = 60;
private int barWidth = 20;
private int rimWidth = 20;
private int textSize = 20;
private float contourSize = 0;

//与页边的间距
private int paddingTop = 5;
private int paddingBottom = 5;
private int paddingLeft = 5;
private int paddingRight = 5;

//View要绘制的颜色
private int barColor = 0xAA000000;
private int contourColor = 0xAA000000;
private int circleColor = 0x00000000;
private int rimColor = 0xAADDDDDD;
private int textColor = 0xFF000000;

//绘制要用的画笔
private Paint barPaint = new Paint();
private Paint circlePaint = new Paint();
private Paint rimPaint = new Paint();
private Paint textPaint = new Paint();
private Paint contourPaint = new Paint();

//绘制要用的矩形
@SuppressWarnings("unused")
private RectF rectBounds = new RectF();
private RectF circleBounds = new RectF();
private RectF circleOuterContour = new RectF();
private RectF circleInnerContour = new RectF();

//动画
//每次绘制要移动的像素数目
private int spinSpeed = 2;
//绘制过程的时间间隔
private int delayMillis = 0;
int progress = 0;
boolean isSpinning = false;

//其他
private String text = "";
private String[] splitText = {};

/**
* ProgressWheel的构造方法
*
* @param context
* @param attrs
*/
public ProgressWheel(Context context, AttributeSet attrs) {
super(context, attrs);

parseAttributes(context.obtainStyledAttributes(attrs,
R.styleable.ProgressWheel));
}

//----------------------------------
//初始化一些元素
//----------------------------------

/*
* 调用这个方法时,使View绘制为方形
* From: http://www.jayway.com/2012/12/12/creating-custom-android-views-part-4-measuring-and-how-to-force-a-view-to-be-square/
* 
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 首先我们要调用超类的onMeasure借口 
// 原因是我们自己去实现一个方法获得长度、宽度太麻烦了
// 使用超类的的方法非常方便而且让复杂的细节可控
super.onMeasure(widthMeasureSpec, heightMeasureSpec);

// 在这里我们不能使用getWidth()和getHeight()。 
    // 因为这两个方法只能在View的布局完成后才能使用,而一个View的绘制过程是先绘制元素,再绘制Layout
    // 所以我们必须使用getMeasuredWidth()和getMeasuredHeight()
    int size = 0;
    int width = getMeasuredWidth();
    int height = getMeasuredHeight();
    int widthWithoutPadding = width - getPaddingLeft() - getPaddingRight();
    int heigthWithoutPadding = height - getPaddingTop() - getPaddingBottom();
    
    // 最后我们用一些简单的逻辑去计算View的大小并调用setMeasuredDimension()去设置View的大小
    // 在比较View的长宽前我们不考虑间距,但当我们设置View所需要绘制的面积时,我们要考虑它
    // 不考虑间距的View(View内的实际画面)此时就应该是方形的,但是由于间距的存在,最终View所占的面积可能不是方形的 
    if (widthWithoutPadding > heigthWithoutPadding) {
      size = heigthWithoutPadding;
    } else {
      size = widthWithoutPadding;
    }
    
    // 如果你重写了onMeasure()方法,你必须调用setMeasuredDimension()方法 
    // 这是你设置View大小的唯一途径  
    // 如果你不调用setMeasuredDimension()方法,父控件会抛出异常,并且程序会崩溃
    // 如果我们使用了超类的onMeasure()方法,我们就不是那么需要setMeasuredDimension()方法
    // 然而,重写onMeasure()方法是为了改变既有的绘制流程,所以我们必须调用setMeasuredDimension()方法以达到我们的目的
    setMeasuredDimension(size + getPaddingLeft() + getPaddingRight(), size + getPaddingTop() + getPaddingBottom());
  }

  /**
   * 使用onSizeChanged方法代替onAttachedToWindow获得View的面积
   * 因为这个方法会在测量了MATCH_PARENT和WRAP_CONTENT后马上被调用
   * 使用获得的面积设置View
   */
  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    // Share the dimensions
    layout_width = w;
    layout_height = h;

    setupBounds();
    setupPaints();
    invalidate();
  }

  /**
   * 设置我们想要绘制的progress wheel的颜色
   */
  private void setupPaints() {
    barPaint.setColor(barColor);
    barPaint.setAntiAlias(true);
    barPaint.setStyle(Style.STROKE);
    barPaint.setStrokeWidth(barWidth);

    rimPaint.setColor(rimColor);
    rimPaint.setAntiAlias(true);
    rimPaint.setStyle(Style.STROKE);
    rimPaint.setStrokeWidth(rimWidth);

    circlePaint.setColor(circleColor);
    circlePaint.setAntiAlias(true);
    circlePaint.setStyle(Style.FILL);

    textPaint.setColor(textColor);
    textPaint.setStyle(Style.FILL);
    textPaint.setAntiAlias(true);
    textPaint.setTextSize(textSize);

    contourPaint.setColor(contourColor);
    contourPaint.setAntiAlias(true);
    contourPaint.setStyle(Style.STROKE);
    contourPaint.setStrokeWidth(contourSize);
  }

  /**
   * 设置元素的边界
   */
  private void setupBounds() {
    // 为了保持宽度和长度的一致,我们要获得layout_width和layout_height中较小的一个,从而绘制一个圆
    int minValue = Math.min(layout_width, layout_height);

    // 计算在绘制过程中在x,y方向的偏移量
    int xOffset = layout_width - minValue;
    int yOffset = layout_height - minValue;

    // 间距加上偏移量
    paddingTop = this.getPaddingTop() + (yOffset / 2);
    paddingBottom = this.getPaddingBottom() + (yOffset / 2);
    paddingLeft = this.getPaddingLeft() + (xOffset / 2);
    paddingRight = this.getPaddingRight() + (xOffset / 2);

    int width = getWidth(); //this.getLayoutParams().width;
    int height = getHeight(); //this.getLayoutParams().height;

    rectBounds = new RectF(paddingLeft,
        paddingTop,
        width - paddingRight,
        height - paddingBottom);

    circleBounds = new RectF(paddingLeft + barWidth,
        paddingTop + barWidth,
        width - paddingRight - barWidth,
        height - paddingBottom - barWidth);
    circleInnerContour = new RectF(circleBounds.left + (rimWidth / 2.0f) + (contourSize / 2.0f), circleBounds.top + (rimWidth / 2.0f) + (contourSize / 2.0f), circleBounds.right - (rimWidth / 2.0f) - (contourSize / 2.0f), circleBounds.bottom - (rimWidth / 2.0f) - (contourSize / 2.0f));
    circleOuterContour = new RectF(circleBounds.left - (rimWidth / 2.0f) - (contourSize / 2.0f), circleBounds.top - (rimWidth / 2.0f) - (contourSize / 2.0f), circleBounds.right + (rimWidth / 2.0f) + (contourSize / 2.0f), circleBounds.bottom + (rimWidth / 2.0f) + (contourSize / 2.0f));

    fullRadius = (width - paddingRight - barWidth) / 2;
    circleRadius = (fullRadius - barWidth) + 1;
  }

  /**
   * 从XML中解析控件的属性
   *
   * @param a the attributes to parse
   */
  private void parseAttributes(TypedArray a) {
    barWidth = (int) a.getDimension(R.styleable.ProgressWheel_barWidth,
        barWidth);

    rimWidth = (int) a.getDimension(R.styleable.ProgressWheel_rimWidth,
        rimWidth);

    spinSpeed = (int) a.getDimension(R.styleable.ProgressWheel_spinSpeed,
        spinSpeed);

    delayMillis = a.getInteger(R.styleable.ProgressWheel_delayMillis,
        delayMillis);
    if (delayMillis < 0) {
      delayMillis = 0;
    }

    barColor = a.getColor(R.styleable.ProgressWheel_barColor, barColor);

    barLength = (int) a.getDimension(R.styleable.ProgressWheel_barLength,
        barLength);

    textSize = (int) a.getDimension(R.styleable.ProgressWheel_textSize,
        textSize);

    textColor = (int) a.getColor(R.styleable.ProgressWheel_textColor,
        textColor);

    //如果text是空的,就无视它
    if (a.hasValue(R.styleable.ProgressWheel_text)) {
      setText(a.getString(R.styleable.ProgressWheel_text));
    }

    rimColor = (int) a.getColor(R.styleable.ProgressWheel_rimColor,
        rimColor);

    circleColor = (int) a.getColor(R.styleable.ProgressWheel_circleColor,
        circleColor);

    contourColor = a.getColor(R.styleable.ProgressWheel_contourColor, contourColor);
    contourSize = a.getDimension(R.styleable.ProgressWheel_contourSize, contourSize);


    // 使用TypedArray获得控件属性时必须要注意:使用结束后必须回收TypedArray的对象
    a.recycle();
  }

  //----------------------------------
  //动画
  //----------------------------------

  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //绘制内圆
    canvas.drawArc(circleBounds, 360, 360, false, circlePaint);
    //绘制边界
    canvas.drawArc(circleBounds, 360, 360, false, rimPaint);
    canvas.drawArc(circleOuterContour, 360, 360, false, contourPaint);
    canvas.drawArc(circleInnerContour, 360, 360, false, contourPaint);
    //绘制条纹
    if (isSpinning) {
      canvas.drawArc(circleBounds, progress - 90, barLength, false,
          barPaint);
    } else {
      canvas.drawArc(circleBounds, -90, progress, false, barPaint);
    }
    //绘制我们想要设置的文字 (并让它显示在圆水平和垂直方向的中心处)
    float textHeight = textPaint.descent() - textPaint.ascent();
    float verticalTextOffset = (textHeight / 2) - textPaint.descent();

    for (String s : splitText) {
      float horizontalTextOffset = textPaint.measureText(s) / 2;
      canvas.drawText(s, this.getWidth() / 2 - horizontalTextOffset,
          this.getHeight() / 2 + verticalTextOffset, textPaint);
    }
    if (isSpinning) {
      scheduleRedraw();
    }
  }

  private void scheduleRedraw() {
    progress += spinSpeed;
    if (progress > 360) {
      progress = 0;
    }
    postInvalidateDelayed(delayMillis);
  }

  /**
  *   判断wheel是否在旋转
  */
  
  public boolean isSpinning() {
    if(isSpinning){
      return true;
    } else {
      return false;
    }
  }
  
  /**
   * 重设进度条的值
   */
  public void resetCount() {
    progress = 0;
    setText("0%");
    invalidate();
  }

  /**
   * 停止进度条的旋转
   */
  public void stopSpinning() {
    isSpinning = false;
    progress = 0;
    postInvalidate();
  }


  /**
   * 让进度条开启旋转模式
   */
  public void spin() {
    isSpinning = true;
    postInvalidate();
  }

  /**
   * 让进度条每次增加1(最大值为360)
   */
  public void incrementProgress() {
    isSpinning = false;
    progress++;
    if (progress > 360)
      progress = 0;
    setText(Math.round(((float) progress / 360) * 100) + "%");
     postInvalidate();
  }


  /**
   * 设置进度条为一个确切的数值
   */
  public void setProgress(int i) {
    isSpinning = false;
    progress = i;
    postInvalidate();
  }

  //----------------------------------
  //get和set方法
  //----------------------------------

  /**
   * 设置progress bar的文字并不需要刷新View
   *
   * @param text the text to show ('
' constitutes a new line)
   */
  public void setText(String text) {
    this.text = text;
    splitText = this.text.split("
");
  }

  public int getCircleRadius() {
    return circleRadius;
  }

  public void setCircleRadius(int circleRadius) {
    this.circleRadius = circleRadius;
  }

  public int getBarLength() {
    return barLength;
  }

  public void setBarLength(int barLength) {
    this.barLength = barLength;
  }

  public int getBarWidth() {
    return barWidth;
  }

  public void setBarWidth(int barWidth) {
    this.barWidth = barWidth;
    
    if ( this.barPaint != null ) {
      this.barPaint.setStrokeWidth( this.barWidth );
    }
  }

  public int getTextSize() {
    return textSize;
  }

  public void setTextSize(int textSize) {
    this.textSize = textSize;
    
    if ( this.textPaint != null ) {
      this.textPaint.setTextSize( this.textSize );
    }
  }

  public int getPaddingTop() {
    return paddingTop;
  }

  public void setPaddingTop(int paddingTop) {
    this.paddingTop = paddingTop;
  }

  public int getPaddingBottom() {
    return paddingBottom;
  }

  public void setPaddingBottom(int paddingBottom) {
    this.paddingBottom = paddingBottom;
  }

  public int getPaddingLeft() {
    return paddingLeft;
  }

  public void setPaddingLeft(int paddingLeft) {
    this.paddingLeft = paddingLeft;
  }

  public int getPaddingRight() {
    return paddingRight;
  }

  public void setPaddingRight(int paddingRight) {
    this.paddingRight = paddingRight;
  }

  public int getBarColor() {
    return barColor;
  }

  public void setBarColor(int barColor) {
    this.barColor = barColor;
    
    if ( this.barPaint != null ) {
      this.barPaint.setColor( this.barColor );
    }
  }

  public int getCircleColor() {
    return circleColor;
  }

  public void setCircleColor(int circleColor) {
    this.circleColor = circleColor;
    
    if ( this.circlePaint != null ) {
      this.circlePaint.setColor( this.circleColor);
    }
  }

  public int getRimColor() {
    return rimColor;
  }

  public void setRimColor(int rimColor) {
    this.rimColor = rimColor;
    
    if ( this.rimPaint != null ) {
      this.rimPaint.setColor( this.rimColor );
    }
  }


  public Shader getRimShader() {
    return rimPaint.getShader();
  }

  public void setRimShader(Shader shader) {
    this.rimPaint.setShader(shader);
  }

  public int getTextColor() {
    return textColor;
  }

  public void setTextColor(int textColor) {
    this.textColor = textColor;
    
    if ( this.textPaint != null ) {
      this.textPaint.setColor( this.textColor );
    }
  }

  public int getSpinSpeed() {
    return spinSpeed;
  }

  public void setSpinSpeed(int spinSpeed) {
    this.spinSpeed = spinSpeed;
  }

  public int getRimWidth() {
    return rimWidth;
  }

  public void setRimWidth(int rimWidth) {
    this.rimWidth = rimWidth;
    
    if ( this.rimPaint != null ) {
      this.rimPaint.setStrokeWidth( this.rimWidth );
    }
  }

  public int getDelayMillis() {
    return delayMillis;
  }

  public void setDelayMillis(int delayMillis) {
    this.delayMillis = delayMillis;
  }
  
  public int getContourColor() {
    return contourColor;
  }
  
  public void setContourColor(int contourColor) {
    this.contourColor = contourColor;
    
    if ( contourPaint != null ) {
      this.contourPaint.setColor( this.contourColor );
    }
  }
  
  public float getContourSize() {
    return this.contourSize;
  }
  
  public void setContourSize(float contourSize) {
    this.contourSize = contourSize;
    
    if ( contourPaint != null ) {
      this.contourPaint.setStrokeWidth( this.contourSize );
    }
  }
}

免责声明:文章转载自《Android自定义控件:进度条的四种实现方式(Progress Wheel的解析)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇linux 创建多个workspaceECharts做为数据展现的一种更灵活直观的方式,Echarts 每种图表制作的适用场景以及优劣势下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

android 6.0权限全面详细分析和解决方案

原文: http://www.2cto.com/kf/201512/455888.html http://blog.csdn.net/yangqingqo/article/details/48371123 http://inthecheesefactory.com/blog/things-you-need-to-know-about-Android-m-p...

android浮动搜索框

android浮动搜索框的配置比较繁琐,需要配置好xml文件才能实现onSearchRequest()方法。 1.配置搜索的XML配置文件​,新建文件searchable.xml,保存在res/xml文件夹中。 1 <?xml version="1.0" encoding="UTF-8"?> 2 3 <searchable androi...

Android https 抓包

背景安卓新版(android 7.0 以上)应用里面,https 的抓包,需要突破 ssl 限制。要突破 ssl 限制又需要 hook ,hook 常用 xposed , xposed 插件有 justtrustme 。(在 低版本 android 7.0 以下的安卓系统上 抓 https 包的话,就不用往下看啦 )要装 xposed , 需要 root...

Xamarin.Android Binding 源自github第三方库的绑定(中级教学)aar文件

今天来个稍微复杂一点的源自github的android第三方库:CarouselView (今天我们依然不用在metadata.xml里面写代码,虽然在build时不会有error出现,但是会有烦人的warning出现,导致我们在dll里面找不到我们需要使用的class) 今天我们就是要解决这个问题,这个问题源自这个项目依赖的库 效果图: 今天我们的任务...

Android Studio AVD中文输入法安装

  我创建了两个AVD,api21与api24,发现均没有中文输入法,上网查询说将键盘选择为“谷歌拼音输入法”,可我的输入法中没有此选项,于是自己决定安装一个 输入法的.apk   首先,在avd中下载,下载成功,无法安装(此时下载的是百度输入法);然后在电脑上下载了搜狗输入法与QQ输入法,利用ADB进行安装 ,报  Failure [INSTALL_FA...

安卓手机清缓存方式

1,安卓手机清除缓存访问:debugx5.qq.com 2, 安卓手机清除缓存三步走:1、点击 debugtbs.qq.com 后退出;2、点击 debugmm.qq.com/?forcex5=true 后退出;3、访问 debugx5.qq.com,在这个页面清除缓存;...