android逆向分析之smali练习

一、工具

第一章节分析了smali的语法

个人比较喜欢Sublime Text 2,多种语言的语法高亮,smali的语法插件



二、java内部内访问外部类的私有属性补习

有的人不懂反编译后出现的this$0或者access$0等,如果你知道,就跳过该小节。

说起这个就要说java内部内是如何访问外部类的私有属性(private)的。

注意一点:java源码经过编译后无论是外部类还是内部类都是单独的文件,只不过内部类和它的外部类在同一目录。

OutClass源码

public class OutClass {

	private int temp1 = 1;
	private int temp2 = 2;
	private int temp3 = 3;

	private static int staticTemp4 = 4;
	private static int staticTemp5 = 5;
	private static int staticTemp6 = 6;

	public int temp7 = 7;

	private void setData(int data) {
		this.temp1 = data;
	}

	static class Inner01 {
		void getData() {
			System.out.println(staticTemp4);
			System.out.println(staticTemp5);
			System.out.println(staticTemp6);

		}
	}

	class Inner02 {
		void getData() {
			System.out.println(temp1);
			System.out.println(temp2);
			System.out.println(temp3);
			System.out.println(temp7);

			setData(12);
		}
	}

}
我们在代码运行时利用反射

OutClass的反射后的代码

class OutClass
{
   public OutClass();

   private void setData(int);
   static int access$0();
   static int access$1();
   static int access$2();
   static int access$3(OutClass);
   static int access$4(OutClass);
   static int access$5(OutClass);
   static void access$6(OutClass,int);

   private int temp1;
   private int temp2;
   private int temp3;
   private static int staticTemp4;
   private static int staticTemp5;
   private static int staticTemp6;
   public int temp7;
}

看的出多了几个access$#的方法。其实access$#和一般的方法一样,但是它是static ,可以通过类名直接访问。

Inner01反射后的代码

Inner01是静态内部类,不会有外部类的引用,所以在构造函数里就不会有OutClass这个引用。


class OutClass$Inner01
{
    OutClass$Inner01();

    void getData();

}

Inner02反射后的代码

Inner02是非静态的,当我们Inner02 inner = new Inner02( )的时候,其实早就换成了

OutClass$Inner02 inner = new  OutClass$Inner02(this);

这样非静态的内部类就把外部类的引用传递进来了,保存在this$0 这个变量中。



class OutClass$Inner02
{
    OutClass$Inner02(OutClass);

    void getData();

   final OutClass this$0;
}

看到了吗!静态内部类与非静态内部类也不一样

为了看的更清楚,我们对class字节码进行反编译,需要知道一点jVM指令

反编译OutClass.class

E:\eclipse-rcp-indigo-SR1-win32-x86_64\workspace\ReflectTest\bin>javap -c OutClass.class
Compiled from "OutClass.java"
public class OutClass {
  public int temp7;

  static {};
    Code:
       0: iconst_4
       1: putstatic     #16                 // Field staticTemp4:I
       4: iconst_5
       5: putstatic     #18                 // Field staticTemp5:I
       8: bipush        6
      10: putstatic     #20                 // Field staticTemp6:I
      13: return

  public OutClass();
    Code:
       0: aload_0                           //aload_0,第一个参数,就是this
       1: invokespecial #25                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: iconst_1
       6: putfield      #27                 // Field temp1:I
       9: aload_0
      10: iconst_2
      11: putfield      #29                 // Field temp2:I
      14: aload_0
      15: iconst_3
      16: putfield      #31                 // Field temp3:I
      19: aload_0
      20: bipush        7
      22: putfield      #33                 // Field temp7:I
      25: return

  static int access$0();                 
    Code:
       0: getstatic     #16                 // Field staticTemp4:I
       3: ireturn

  static int access$1();
    Code:
       0: getstatic     #18                 // Field staticTemp5:I
       3: ireturn

  static int access$2();
    Code:
       0: getstatic     #20                 // Field staticTemp6:I
       3: ireturn

  static int access$3(OutClass);
    Code:
       0: aload_0
       1: getfield      #27                 // Field temp1:I
       4: ireturn

  static int access$4(OutClass);
    Code:
       0: aload_0
       1: getfield      #29                 // Field temp2:I
       4: ireturn

  static int access$5(OutClass);
    Code:
       0: aload_0
       1: getfield      #31                 // Field temp3:I
       4: ireturn

  static void access$6(OutClass, int);
    Code:
       0: aload_0
       1: iload_1
       2: invokespecial #50                 // Method setData:(I)V
       5: return
}


反编译Inner01.class


E:\eclipse-rcp-indigo-SR1-win32-x86_64\workspace\ReflectTest\bin>javap -c OutClass$Inner01.class
Compiled from "OutClass.java"
class OutClass$Inner01 {
  OutClass$Inner01();
    Code:
       0: aload_0                             
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  void getData();
    Code:
       0: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: invokestatic  #21                 // Method OutClass.access$0:()I
       6: invokevirtual #27                 // Method java/io/PrintStream.println:(I)V
       9: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
      12: invokestatic  #33                 // Method OutClass.access$1:()I
      15: invokevirtual #27                 // Method java/io/PrintStream.println:(I)V
      18: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
      21: invokestatic  #36                 // Method OutClass.access$2:()I
      24: invokevirtual #27                 // Method java/io/PrintStream.println:(I)V
      27: return
}

反编译Inner02.class

E:\eclipse-rcp-indigo-SR1-win32-x86_64\workspace\ReflectTest\bin>javap -c OutClass$Inner02.class
Compiled from "OutClass.java"
class OutClass$Inner02 {
  final OutClass this$0;

  OutClass$Inner02(OutClass);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #10                 // Field this$0:LOutClass;
       5: aload_0
       6: invokespecial #12                 // Method java/lang/Object."<init>":()V
       9: return

  void getData();
    Code:
       0: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: aload_0
       4: getfield      #10                 // Field this$0:LOutClass;
       7: invokestatic  #26                 // Method OutClass.access$3:(LOutClass;)I
      10: invokevirtual #32                 // Method java/io/PrintStream.println:(I)V
      13: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
      16: aload_0
      17: getfield      #10                 // Field this$0:LOutClass;
      20: invokestatic  #38                 // Method OutClass.access$4:(LOutClass;)I
      23: invokevirtual #32                 // Method java/io/PrintStream.println:(I)V
      26: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
      29: aload_0
      30: getfield      #10                 // Field this$0:LOutClass;
      33: invokestatic  #41                 // Method OutClass.access$5:(LOutClass;)I
      36: invokevirtual #32                 // Method java/io/PrintStream.println:(I)V
      39: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;
      42: aload_0
      43: getfield      #10                 // Field this$0:LOutClass;
      46: getfield      #44                 // Field OutClass.temp7:I
      49: invokevirtual #32                 // Method java/io/PrintStream.println:(I)V
      52: aload_0
      53: getfield      #10                 // Field this$0:LOutClass;
      56: bipush        12
      58: invokestatic  #48                 // Method OutClass.access$6:(LOutClass;I)V
      61: return
}

    非静态内部类中会有一个外部类的引用,this$0(内部类的嵌套可能会叫this$1),但是不能靠这个引用来访问外部类的私有域。那么非静态内部类是如何大摇大摆的访问外部类的私有域的呢?


  1)、非静态内部类访问外部类的字段,

     System.out.println(temp2);

      16: aload_0                           // this指的是<span style="font-size: 18px;">Inner02</span><span style="font-size: 14px; font-family: Arial, Helvetica, sans-serif;">这个类的实例</span>
      17: getfield      #10                 // Field this$0:LOutClass;
      20: invokestatic  #38                 // Method OutClass.access$4:(LOutClass;)I
      23: invokevirtual #32                 // Method java/io/PrintStream.println:(I)V
      26: getstatic     #20                 // Field java/lang/System.out:Ljava/io/PrintStream;

其实就是this$0.access$4( this$0)来获取temp2的值。 access$#都是静态的,所以用类的实例来调用是可以的。

  2)、非静态内部类访问外部类的私有方法

  setData(12);

      52: aload_0                            //<span style="font-family: Arial, Helvetica, sans-serif;">this指的是</span><span style="font-family: Arial, Helvetica, sans-serif;"><span style="font-size:14px;">Inner02这个类的实例</span></span>
      53: getfield      #10                 // Field this$0:LOutClass;
      56: bipush        12
      58: invokestatic  #48                 // Method OutClass.access$6:(LOutClass;I)V
其实就是调用this$0.access$6( this$0,12 ),将参数12传递过去。

  3)、静态内部类访问外部类的字段

           静态内部类只能访问外部类的静态属性(static,因为没有外部类的引用,只能靠外部类的类名访问)。

           静态内部类是没有外部类的引用的

    System.out.println(staticTemp5);

       9: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
      12: invokestatic  #33                 // Method OutClass.access$1:()I
      15: invokevirtual #27                 // Method java/io/PrintStream.println:(I)V
      
   虽然没有引用,但是靠外部类的类名访问相应的access$#(  )方法可以操控外部类的静态字段。


  4)、静态内部类访问外部类的方法

          只能访问外部类的静方法,依然靠access$#(  )访问。


总结:静态的还是非静态的都是靠access方法访问,至于$后面的数字是根据私有属性的初始化位置来的(静态的早于非静态)。

smali里使内部类访问外部类的私有成员,编译器生成了形似 “外部类.access$XYZ”的函数。XYZ为数字。X是按照私有成员在内部类出现的顺序递增的。YZ为02的话,标明是基本变量成员;YZ为00的话标明是对象成员或者函数。


三、练习

我们拿魅族的应用中心做试验,来反编译一个可以折叠的TextView。

.class public Lcom/meizu/common/widget/FoldableTextView;
.super Landroid/widget/TextView;
.source "FoldableTextView.java"

# interfaces
.implements Landroid/view/View$OnClickListener;


# annotations
.annotation system Ldalvik/annotation/MemberClasses;
    value = {
        Lcom/meizu/common/widget/FoldableTextView$FoldingListener;,
        Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan;
    }
.end annotation


# static fields
.field private static final DEBUG:Z = false

.field private static final ELLIPSIS_TWO_DOTS:Ljava/lang/String; = "\u2025"

.field private static final TAG:Ljava/lang/String; = "FoldableTextView"


# instance fields
.field private mAlignViewEdge:Z

.field private mClickToFold:Z

.field private mEllipseText:Ljava/lang/CharSequence;

.field private mFoldLineMax:I

.field private mIsClickSpan:Z

.field private mIsfolded:Z

.field private mLinkColor:I

.field private mListener:Lcom/meizu/common/widget/FoldableTextView$FoldingListener;

.field private mUnfoldText:Ljava/lang/CharSequence;

.field private mainText:Ljava/lang/CharSequence;


# direct methods
.method public constructor <init>(Landroid/content/Context;)V
    .locals 1
    .param p1, "context"    # Landroid/content/Context;

    .prologue
    .line 48
    const/4 v0, 0x0

    invoke-direct {p0, p1, v0}, Lcom/meizu/common/widget/FoldableTextView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V

    .line 49
    return-void
.end method

.method public constructor <init>(Landroid/content/Context;Landroid/util/AttributeSet;)V
    .locals 1
    .param p1, "context"    # Landroid/content/Context;
    .param p2, "attrs"    # Landroid/util/AttributeSet;

    .prologue
    .line 52
    sget v0, Lcom/meizu/common/R$style;->Widget_MeizuCommon_FoldableTextView:I

    invoke-direct {p0, p1, p2, v0}, Lcom/meizu/common/widget/FoldableTextView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;I)V

    .line 53
    return-void
.end method

.method public constructor <init>(Landroid/content/Context;Landroid/util/AttributeSet;I)V
    .locals 8
    .param p1, "context"    # Landroid/content/Context;
    .param p2, "attrs"    # Landroid/util/AttributeSet;
    .param p3, "defStyle"    # I

    .prologue
    const/4 v7, -0x1

    const/4 v6, 0x1

    const/4 v5, 0x0

    .line 56
    invoke-direct {p0, p1, p2, p3}, Landroid/widget/TextView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;I)V

    .line 39
    iput-boolean v6, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z

    .line 40
    iput-boolean v5, p0, Lcom/meizu/common/widget/FoldableTextView;->mAlignViewEdge:Z

    .line 41
    iput-boolean v5, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsClickSpan:Z

    .line 42
    iput-boolean v6, p0, Lcom/meizu/common/widget/FoldableTextView;->mClickToFold:Z

    .line 43
    iput v7, p0, Lcom/meizu/common/widget/FoldableTextView;->mLinkColor:I

    .line 57
    sget-object v4, Lcom/meizu/common/R$styleable;->FoldableTextView:[I

    invoke-virtual {p1, p2, v4, p3, v5}, Landroid/content/Context;->obtainStyledAttributes(Landroid/util/AttributeSet;[III)Landroid/content/res/TypedArray;

    move-result-object v0

    .line 59
    .local v0, "a":Landroid/content/res/TypedArray;
    invoke-virtual {v0}, Landroid/content/res/TypedArray;->getIndexCount()I

    move-result v3

    .line 60
    .local v3, "n":I
    const/4 v2, 0x0

    .local v2, "i":I
    :goto_0
    if-ge v2, v3, :cond_7

    .line 61
    invoke-virtual {v0, v2}, Landroid/content/res/TypedArray;->getIndex(I)I

    move-result v1

    .line 62
    .local v1, "attr":I
    sget v4, Lcom/meizu/common/R$styleable;->FoldableTextView_mzTextEllipse:I

    if-ne v1, v4, :cond_1

    .line 63
    invoke-virtual {v0, v1}, Landroid/content/res/TypedArray;->getText(I)Ljava/lang/CharSequence;

    move-result-object v4

    iput-object v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mEllipseText:Ljava/lang/CharSequence;

    .line 60
    :cond_0
    :goto_1
    add-int/lit8 v2, v2, 0x1

    goto :goto_0

    .line 64
    :cond_1
    sget v4, Lcom/meizu/common/R$styleable;->FoldableTextView_mzTextUnfold:I

    if-ne v1, v4, :cond_2

    .line 65
    invoke-virtual {v0, v1}, Landroid/content/res/TypedArray;->getText(I)Ljava/lang/CharSequence;

    move-result-object v4

    iput-object v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mUnfoldText:Ljava/lang/CharSequence;

    goto :goto_1

    .line 66
    :cond_2
    sget v4, Lcom/meizu/common/R$styleable;->FoldableTextView_mzMaxFoldLine:I

    if-ne v1, v4, :cond_3

    .line 67
    invoke-virtual {v0, v1, v5}, Landroid/content/res/TypedArray;->getInt(II)I

    move-result v4

    iput v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mFoldLineMax:I

    goto :goto_1

    .line 68
    :cond_3
    sget v4, Lcom/meizu/common/R$styleable;->FoldableTextView_mzUnfoldAlignViewEdge:I

    if-ne v1, v4, :cond_4

    .line 69
    invoke-virtual {v0, v1, v5}, Landroid/content/res/TypedArray;->getBoolean(IZ)Z

    move-result v4

    iput-boolean v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mAlignViewEdge:Z

    goto :goto_1

    .line 70
    :cond_4
    sget v4, Lcom/meizu/common/R$styleable;->FoldableTextView_mzClickToFold:I

    if-ne v1, v4, :cond_5

    .line 71
    invoke-virtual {v0, v1, v5}, Landroid/content/res/TypedArray;->getBoolean(IZ)Z

    move-result v4

    iput-boolean v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mClickToFold:Z

    goto :goto_1

    .line 72
    :cond_5
    sget v4, Lcom/meizu/common/R$styleable;->FoldableTextView_mzLinkColor:I

    if-ne v1, v4, :cond_6

    .line 73
    invoke-virtual {v0, v1, v7}, Landroid/content/res/TypedArray;->getColor(II)I

    move-result v4

    iput v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mLinkColor:I

    goto :goto_1

    .line 74
    :cond_6
    sget v4, Lcom/meizu/common/R$styleable;->FoldableTextView_mzIsFold:I

    if-ne v1, v4, :cond_0

    .line 75
    invoke-virtual {v0, v1, v6}, Landroid/content/res/TypedArray;->getBoolean(IZ)Z

    move-result v4

    iput-boolean v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z

    goto :goto_1

    .line 78
    .end local v1    # "attr":I
    :cond_7
    invoke-virtual {v0}, Landroid/content/res/TypedArray;->recycle()V

    .line 79
    iget-object v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mUnfoldText:Ljava/lang/CharSequence;

    invoke-static {v4}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z

    move-result v4

    if-eqz v4, :cond_8

    .line 80
    sget v4, Lcom/meizu/common/R$string;->more_item_label:I

    invoke-virtual {p1, v4}, Landroid/content/Context;->getString(I)Ljava/lang/String;

    move-result-object v4

    iput-object v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mUnfoldText:Ljava/lang/CharSequence;

    .line 85
    :cond_8
    iget-object v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mEllipseText:Ljava/lang/CharSequence;

    invoke-static {v4}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z

    move-result v4

    if-eqz v4, :cond_9

    .line 86
    const-string v4, "\u2025"

    iput-object v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mEllipseText:Ljava/lang/CharSequence;

    .line 89
    :cond_9
    invoke-static {}, Landroid/text/method/LinkMovementMethod;->getInstance()Landroid/text/method/MovementMethod;

    move-result-object v4

    invoke-virtual {p0, v4}, Lcom/meizu/common/widget/FoldableTextView;->setMovementMethod(Landroid/text/method/MovementMethod;)V

    .line 90
    invoke-direct {p0, v6}, Lcom/meizu/common/widget/FoldableTextView;->setOnClickListener(Z)V

    .line 91
    return-void
.end method

.method static synthetic access$100(Lcom/meizu/common/widget/FoldableTextView;)I
    .locals 1
    .param p0, "x0"    # Lcom/meizu/common/widget/FoldableTextView;

    .prologue
    .line 33
    iget v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mLinkColor:I

    return v0
.end method

.method static synthetic access$200(Lcom/meizu/common/widget/FoldableTextView;)Lcom/meizu/common/widget/FoldableTextView$FoldingListener;
    .locals 1
    .param p0, "x0"    # Lcom/meizu/common/widget/FoldableTextView;

    .prologue
    .line 33
    iget-object v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mListener:Lcom/meizu/common/widget/FoldableTextView$FoldingListener;

    return-object v0
.end method

.method static synthetic access$302(Lcom/meizu/common/widget/FoldableTextView;Z)Z
    .locals 0
    .param p0, "x0"    # Lcom/meizu/common/widget/FoldableTextView;
    .param p1, "x1"    # Z

    .prologue
    .line 33
    iput-boolean p1, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z

    return p1
.end method

.method static synthetic access$402(Lcom/meizu/common/widget/FoldableTextView;Z)Z
    .locals 0
    .param p0, "x0"    # Lcom/meizu/common/widget/FoldableTextView;
    .param p1, "x1"    # Z

    .prologue
    .line 33
    iput-boolean p1, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsClickSpan:Z

    return p1
.end method

.method private foldText(Ljava/lang/CharSequence;)Ljava/lang/CharSequence;
    .locals 14
    .param p1, "text"    # Ljava/lang/CharSequence;

    .prologue
    .line 205
    invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->getLayout()Landroid/text/Layout;

    move-result-object v11

    .line 206
    .local v11, "layout":Landroid/text/Layout;
    new-instance v1, Landroid/text/SpannableStringBuilder;

    invoke-direct {v1, p1}, Landroid/text/SpannableStringBuilder;-><init>(Ljava/lang/CharSequence;)V

    .line 207
    .local v1, "sb":Landroid/text/SpannableStringBuilder;
    new-instance v0, Landroid/text/DynamicLayout;

    invoke-virtual {v11}, Landroid/text/Layout;->getPaint()Landroid/text/TextPaint;

    move-result-object v2

    invoke-virtual {v11}, Landroid/text/Layout;->getWidth()I

    move-result v3

    invoke-virtual {v11}, Landroid/text/Layout;->getAlignment()Landroid/text/Layout$Alignment;

    move-result-object v4

    const/high16 v5, 0x3f800000    # 1.0f

    const/4 v6, 0x0

    const/4 v7, 0x0

    invoke-direct/range {v0 .. v7}, Landroid/text/DynamicLayout;-><init>(Ljava/lang/CharSequence;Landroid/text/TextPaint;ILandroid/text/Layout$Alignment;FFZ)V

    .line 209
    .local v0, "tmpLayout":Landroid/text/DynamicLayout;
    invoke-virtual {v0}, Landroid/text/DynamicLayout;->getLineCount()I

    move-result v2

    iget v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mFoldLineMax:I



    if-gt v2, v3, :cond_0




    .line 253
    .end local p1    # "text":Ljava/lang/CharSequence;
    :goto_0
    return-object p1

    .line 214
    .restart local p1    # "text":Ljava/lang/CharSequence;
    :cond_0
    iget v13, p0, Lcom/meizu/common/widget/FoldableTextView;->mFoldLineMax:I

    .line 215
    .local v13, "lineMax":I
    :goto_1
    const/4 v2, 0x1

    if-le v13, v2, :cond_1

    .line 216
    add-int/lit8 v12, v13, -0x1

    .line 217
    .local v12, "line":I
    invoke-virtual {v0, v12}, Landroid/text/DynamicLayout;->getLineStart(I)I

    move-result v2

    invoke-virtual {v0, v12}, Landroid/text/DynamicLayout;->getLineVisibleEnd(I)I

    move-result v3

    if-ge v2, v3, :cond_4

    .line 223
    .end local v12    # "line":I
    :cond_1
    add-int/lit8 v2, v13, -0x1

    invoke-virtual {v0, v2}, Landroid/text/DynamicLayout;->getLineVisibleEnd(I)I

    move-result v10

    .line 224
    .local v10, "en":I
    iget-object v2, p0, Lcom/meizu/common/widget/FoldableTextView;->mEllipseText:Ljava/lang/CharSequence;

    invoke-static {v2}, Landroid/text/TextUtils;->isEmpty(Ljava/lang/CharSequence;)Z

    move-result v2

    if-eqz v2, :cond_5

    .line 225
    invoke-virtual {v1}, Landroid/text/SpannableStringBuilder;->length()I

    move-result v2

    invoke-virtual {v1, v10, v2}, Landroid/text/SpannableStringBuilder;->delete(II)Landroid/text/SpannableStringBuilder;

    .line 229
    :goto_2
    const/16 v2, 0x20

    invoke-virtual {v1, v2}, Landroid/text/SpannableStringBuilder;->append(C)Landroid/text/SpannableStringBuilder;

    .line 231
    invoke-virtual {v1}, Landroid/text/SpannableStringBuilder;->length()I

    move-result v8

    .line 232
    .local v8, "addIndex":I
    iget-object v2, p0, Lcom/meizu/common/widget/FoldableTextView;->mUnfoldText:Ljava/lang/CharSequence;

    invoke-virtual {v1, v2}, Landroid/text/SpannableStringBuilder;->append(Ljava/lang/CharSequence;)Landroid/text/SpannableStringBuilder;

    .line 233
    new-instance v2, Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan;

    invoke-direct {v2, p0, p1}, Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan;-><init>(Lcom/meizu/common/widget/FoldableTextView;Ljava/lang/CharSequence;)V

    invoke-virtual {v1}, Landroid/text/SpannableStringBuilder;->length()I

    move-result v3

    const/16 v4, 0x21

    invoke-virtual {v1, v2, v8, v3, v4}, Landroid/text/SpannableStringBuilder;->setSpan(Ljava/lang/Object;III)V

    .line 236
    if-lez v10, :cond_6

    invoke-virtual {v0}, Landroid/text/DynamicLayout;->getLineCount()I

    move-result v2

    if-le v2, v13, :cond_6

    .line 237
    move v9, v10

    .line 239
    .local v9, "delIndex":I
    :cond_2
    add-int/lit8 v9, v9, -0x1

    .line 240
    add-int/lit8 v2, v9, 0x1

    invoke-virtual {v1, v9, v2}, Landroid/text/SpannableStringBuilder;->delete(II)Landroid/text/SpannableStringBuilder;

    .line 241
    if-lez v9, :cond_3

    invoke-virtual {v0}, Landroid/text/DynamicLayout;->getLineCount()I

    move-result v2

    if-gt v2, v13, :cond_2

    .end local v9    # "delIndex":I
    :cond_3
    :goto_3
    move-object p1, v1

    .line 253
    goto :goto_0

    .line 220
    .end local v8    # "addIndex":I
    .end local v10    # "en":I
    .restart local v12    # "line":I
    :cond_4
    move v13, v12

    .line 221
    goto :goto_1

    .line 227
    .end local v12    # "line":I
    .restart local v10    # "en":I
    :cond_5
    invoke-virtual {v1}, Landroid/text/SpannableStringBuilder;->length()I

    move-result v2

    iget-object v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mEllipseText:Ljava/lang/CharSequence;

    invoke-virtual {v1, v10, v2, v3}, Landroid/text/SpannableStringBuilder;->replace(IILjava/lang/CharSequence;)Landroid/text/SpannableStringBuilder;

    goto :goto_2

    .line 242
    .restart local v8    # "addIndex":I
    :cond_6
    iget-boolean v2, p0, Lcom/meizu/common/widget/FoldableTextView;->mAlignViewEdge:Z

    if-eqz v2, :cond_3

    .line 243
    :goto_4
    invoke-virtual {v0}, Landroid/text/DynamicLayout;->getLineCount()I

    move-result v2

    if-ne v2, v13, :cond_3

    .line 244
    const-string v2, " "

    invoke-virtual {v1, v8, v8, v2}, Landroid/text/SpannableStringBuilder;->replace(IILjava/lang/CharSequence;)Landroid/text/SpannableStringBuilder;

    .line 245
    invoke-virtual {v0}, Landroid/text/DynamicLayout;->getLineCount()I

    move-result v2

    if-le v2, v13, :cond_7

    .line 246
    add-int/lit8 v2, v8, 0x1

    invoke-virtual {v1, v8, v2}, Landroid/text/SpannableStringBuilder;->delete(II)Landroid/text/SpannableStringBuilder;

    goto :goto_3

    .line 249
    :cond_7
    add-int/lit8 v8, v8, 0x1

    goto :goto_4
.end method

.method private setOnClickListener(Z)V
    .locals 1
    .param p1, "set"    # Z

    .prologue
    .line 285
    if-eqz p1, :cond_0

    .line 286
    invoke-virtual {p0, p0}, Lcom/meizu/common/widget/FoldableTextView;->setOnClickListener(Landroid/view/View$OnClickListener;)V

    .line 290
    :goto_0
    return-void

    .line 288
    :cond_0
    const/4 v0, 0x0

    invoke-virtual {p0, v0}, Lcom/meizu/common/widget/FoldableTextView;->setOnClickListener(Landroid/view/View$OnClickListener;)V

    goto :goto_0
.end method


# virtual methods
.method public getFoldStatus()Z
    .locals 1

    .prologue
    .line 155
    iget-boolean v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z

    return v0
.end method

.method public onClick(Landroid/view/View;)V
    .locals 3
    .param p1, "v"    # Landroid/view/View;

    .prologue
    const/4 v2, 0x1

    const/4 v1, 0x0

    .line 304
    iget-boolean v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsClickSpan:Z

    if-eqz v0, :cond_1

    .line 305
    iput-boolean v1, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsClickSpan:Z

    .line 329
    :cond_0
    :goto_0
    return-void

    .line 308
    :cond_1
    iget-boolean v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mClickToFold:Z

    if-eqz v0, :cond_0

    .line 312
    iget-boolean v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z

    if-eqz v0, :cond_3

    .line 314
    iget-object v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mListener:Lcom/meizu/common/widget/FoldableTextView$FoldingListener;

    if-eqz v0, :cond_2

    iget-object v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mListener:Lcom/meizu/common/widget/FoldableTextView$FoldingListener;

    invoke-interface {v0, p0, v1}, Lcom/meizu/common/widget/FoldableTextView$FoldingListener;->onFolding(Lcom/meizu/common/widget/FoldableTextView;Z)Z

    move-result v0

    if-eqz v0, :cond_0

    .line 317
    :cond_2
    iput-boolean v1, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z

    .line 318
    iget-object v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mainText:Ljava/lang/CharSequence;

    sget-object v1, Landroid/widget/TextView$BufferType;->NORMAL:Landroid/widget/TextView$BufferType;

    invoke-virtual {p0, v0, v1}, Lcom/meizu/common/widget/FoldableTextView;->setText(Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;)V

    goto :goto_0

    .line 322
    :cond_3
    iget-object v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mListener:Lcom/meizu/common/widget/FoldableTextView$FoldingListener;

    if-eqz v0, :cond_4

    iget-object v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mListener:Lcom/meizu/common/widget/FoldableTextView$FoldingListener;

    invoke-interface {v0, p0, v2}, Lcom/meizu/common/widget/FoldableTextView$FoldingListener;->onFolding(Lcom/meizu/common/widget/FoldableTextView;Z)Z

    move-result v0

    if-eqz v0, :cond_0

    .line 325
    :cond_4
    iput-boolean v2, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z

    .line 326
    invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->requestLayout()V

    .line 327
    invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->invalidate()V

    goto :goto_0
.end method

.method protected onMeasure(II)V
    .locals 7
    .param p1, "widthMeasureSpec"    # I
    .param p2, "heightMeasureSpec"    # I

    .prologue
    const/4 v6, 0x0

    .line 179
    invoke-super {p0, p1, p2}, Landroid/widget/TextView;->onMeasure(II)V

    .line 180
    iget-boolean v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z

    if-eqz v3, :cond_0

    iget v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mFoldLineMax:I

    if-gtz v3, :cond_1

    .line 202
    :cond_0
    :goto_0
    return-void

    .line 184
    :cond_1
    invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->getText()Ljava/lang/CharSequence;

    move-result-object v2

    .line 185
    .local v2, "text":Ljava/lang/CharSequence;
    iput-object v2, p0, Lcom/meizu/common/widget/FoldableTextView;->mainText:Ljava/lang/CharSequence;

    .line 186
    iget-object v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mainText:Ljava/lang/CharSequence;

    if-eqz v3, :cond_2

    iget-object v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mainText:Ljava/lang/CharSequence;

    instance-of v3, v3, Landroid/text/Spanned;

    if-eqz v3, :cond_2

    .line 187
    iget-object v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mainText:Ljava/lang/CharSequence;

    check-cast v3, Landroid/text/Spanned;

    iget-object v4, p0, Lcom/meizu/common/widget/FoldableTextView;->mainText:Ljava/lang/CharSequence;

    invoke-interface {v4}, Ljava/lang/CharSequence;->length()I

    move-result v4

    const-class v5, Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan;

    invoke-interface {v3, v6, v4, v5}, Landroid/text/Spanned;->getSpans(IILjava/lang/Class;)[Ljava/lang/Object;

    move-result-object v1

    check-cast v1, [Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan;

    .line 188
    .local v1, "spans":[Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan;
    if-eqz v1, :cond_2

    array-length v3, v1

    if-lez v3, :cond_2

    .line 189
    aget-object v3, v1, v6

    # getter for: Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan;->mText:Ljava/lang/CharSequence;
    invoke-static {v3}, Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan;->access$000(Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan;)Ljava/lang/CharSequence;

    move-result-object v3

    iput-object v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mainText:Ljava/lang/CharSequence;

    .line 193
    .end local v1    # "spans":[Lcom/meizu/common/widget/FoldableTextView$MoreClickSpan;
    :cond_2
    iget-object v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mainText:Ljava/lang/CharSequence;

    invoke-direct {p0, v3}, Lcom/meizu/common/widget/FoldableTextView;->foldText(Ljava/lang/CharSequence;)Ljava/lang/CharSequence;

    move-result-object v0

    .line 194
    .local v0, "dst":Ljava/lang/CharSequence;
    invoke-static {v2, v0}, Landroid/text/TextUtils;->equals(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Z

    move-result v3

    if-nez v3, :cond_0

    .line 196
    iget-object v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mListener:Lcom/meizu/common/widget/FoldableTextView$FoldingListener;

    if-eqz v3, :cond_3

    iget-object v3, p0, Lcom/meizu/common/widget/FoldableTextView;->mListener:Lcom/meizu/common/widget/FoldableTextView$FoldingListener;

    const/4 v4, 0x1

    invoke-interface {v3, p0, v4}, Lcom/meizu/common/widget/FoldableTextView$FoldingListener;->onFolding(Lcom/meizu/common/widget/FoldableTextView;Z)Z

    move-result v3

    if-eqz v3, :cond_0

    .line 199
    :cond_3
    sget-object v3, Landroid/widget/TextView$BufferType;->SPANNABLE:Landroid/widget/TextView$BufferType;

    invoke-virtual {p0, v0, v3}, Lcom/meizu/common/widget/FoldableTextView;->setText(Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;)V

    .line 200
    invoke-super {p0, p1, p2}, Landroid/widget/TextView;->onMeasure(II)V

    goto :goto_0
.end method

.method public setClickToFold(Z)V
    .locals 1
    .param p1, "enabled"    # Z

    .prologue
    .line 133
    if-eqz p1, :cond_0

    .line 134
    const/4 v0, 0x1

    iput-boolean v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mClickToFold:Z

    .line 138
    :goto_0
    return-void

    .line 136
    :cond_0
    const/4 v0, 0x0

    iput-boolean v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mClickToFold:Z

    goto :goto_0
.end method

.method public setFoldStatus(Z)V
    .locals 1
    .param p1, "fold"    # Z

    .prologue
    .line 164
    iget-boolean v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z

    if-eq v0, p1, :cond_0

    .line 165
    iput-boolean p1, p0, Lcom/meizu/common/widget/FoldableTextView;->mIsfolded:Z

    .line 166
    invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->requestLayout()V

    .line 167
    invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->invalidate()V

    .line 169
    :cond_0
    return-void
.end method

.method public setFoldText(Ljava/lang/String;Ljava/lang/String;Z)V
    .locals 0
    .param p1, "strEllipse"    # Ljava/lang/String;
    .param p2, "strUnfold"    # Ljava/lang/String;
    .param p3, "alignViewEdge"    # Z

    .prologue
    .line 101
    iput-boolean p3, p0, Lcom/meizu/common/widget/FoldableTextView;->mAlignViewEdge:Z

    .line 102
    if-eqz p1, :cond_0

    .line 103
    iput-object p1, p0, Lcom/meizu/common/widget/FoldableTextView;->mEllipseText:Ljava/lang/CharSequence;

    .line 105
    :cond_0
    if-eqz p2, :cond_1

    .line 106
    iput-object p2, p0, Lcom/meizu/common/widget/FoldableTextView;->mUnfoldText:Ljava/lang/CharSequence;

    .line 110
    :cond_1
    return-void
.end method

.method public setFolding(ILcom/meizu/common/widget/FoldableTextView$FoldingListener;)V
    .locals 1
    .param p1, "lineMax"    # I
    .param p2, "l"    # Lcom/meizu/common/widget/FoldableTextView$FoldingListener;

    .prologue
    .line 120
    iput p1, p0, Lcom/meizu/common/widget/FoldableTextView;->mFoldLineMax:I

    .line 121
    iput-object p2, p0, Lcom/meizu/common/widget/FoldableTextView;->mListener:Lcom/meizu/common/widget/FoldableTextView$FoldingListener;

    .line 122
    iget v0, p0, Lcom/meizu/common/widget/FoldableTextView;->mFoldLineMax:I

    if-lez v0, :cond_0

    .line 123
    invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->requestLayout()V

    .line 124
    invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->invalidate()V

    .line 126
    :cond_0
    return-void
.end method

.method public setLinkColor(I)V
    .locals 0
    .param p1, "color"    # I

    .prologue
    .line 146
    iput p1, p0, Lcom/meizu/common/widget/FoldableTextView;->mLinkColor:I

    .line 147
    invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->invalidate()V

    .line 148
    return-void
.end method

.method public setText(Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;)V
    .locals 0
    .param p1, "text"    # Ljava/lang/CharSequence;
    .param p2, "type"    # Landroid/widget/TextView$BufferType;

    .prologue
    .line 173
    invoke-super {p0, p1, p2}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;)V

    .line 174
    invoke-virtual {p0}, Lcom/meizu/common/widget/FoldableTextView;->requestLayout()V

    .line 175
    return-void
.end method

大致的java代码。为什么说大致,有的逻辑不是很好懂,有的地方按照我自己的逻辑写的,

正真的源码可能和我的还是有差别的。

public class FoldableTextView extends TextView implements View.OnClickListener {

	private static final String ELLIPSIS_TWO_DOTS = "‥";
	/** 是否边缘对齐 */
	private boolean mAlignViewEdge = false;
	/** 是否能折叠 */
	private boolean mClickToFold = true;
	private CharSequence mEllipseText;   //..
	private CharSequence mUnfoldText;  //更多
	/**折叠后显示的最多行数*/
	private int mFoldLineMax;           //显示的最多行数
	
	private boolean mIsClickSpan = false;
	/**是否是折叠状态*/
	private boolean mIsfolded = true;   
	private int mLinkColor = -1;
	
	
	private FoldingListener mListener;
	private CharSequence mainText;

	public FoldableTextView(Context paramContext) {
		this(paramContext, null);
	}

	public FoldableTextView(Context paramContext, AttributeSet paramAttributeSet) {
		this(paramContext, paramAttributeSet,
				R.style.Widget_MeizuCommon_FoldableTextView);
	}

	public FoldableTextView(Context context, AttributeSet paramAttributeSet,
			int paramInt) {
		super(context, paramAttributeSet, paramInt);
		TypedArray localTypedArray = context.obtainStyledAttributes(
				paramAttributeSet, R.styleable.FoldableTextView, paramInt, 0);
		mEllipseText = localTypedArray
				.getText(R.styleable.FoldableTextView_mzTextEllipse);
		mUnfoldText = localTypedArray
				.getText(R.styleable.FoldableTextView_mzTextUnfold);
		mFoldLineMax = localTypedArray.getInt(
				R.styleable.FoldableTextView_mzMaxFoldLine, 0);
		mAlignViewEdge = localTypedArray.getBoolean(
				R.styleable.FoldableTextView_mzUnfoldAlignViewEdge, false);
		mClickToFold = localTypedArray.getBoolean(
				R.styleable.FoldableTextView_mzClickToFold, false);
		mLinkColor = localTypedArray.getColor(
				R.styleable.FoldableTextView_mzLinkColor, -1);
		mIsfolded = localTypedArray.getBoolean(
				R.styleable.FoldableTextView_mzIsFold, true);
		localTypedArray.recycle();
		if (TextUtils.isEmpty(this.mUnfoldText)) {
			this.mUnfoldText = context.getString(R.string.more_item_label);
		}
		if (TextUtils.isEmpty(this.mEllipseText)) {
			this.mEllipseText = ELLIPSIS_TWO_DOTS;
		}
		setMovementMethod(LinkMovementMethod.getInstance());
		setOnClickListener(true);
	}

	
	SpannableStringBuilder sb ;
	private CharSequence foldText(CharSequence text) {
		if(sb!=null && sb.length()>0) return sb;
		 sb = new SpannableStringBuilder(text);
		Layout layout = this.getLayout();
		
	   
		DynamicLayout tmpLayout = new DynamicLayout(sb, layout.getPaint(),
				layout.getWidth(), layout.getAlignment(), 1.0f, 0, false);
		if (tmpLayout.getLineCount() <= mFoldLineMax) return sb;
		//行数是按照0开始计数的?如果是则返回偏移值。偏移值是按1开始计数的,
		//如果想获取该返回值那里 的字符(或者字符串中的位置),记得-1
		int en = tmpLayout.getLineVisibleEnd(mFoldLineMax-1);
		
		MoreClickSpan span = new MoreClickSpan(sb);
		if (!TextUtils.isEmpty(mEllipseText)) {
			sb.replace(en, sb.length(), mEllipseText);
		}else {
			sb.delete(en, sb.length());
		}
		
		sb.append(" ");
		int addIndex = sb.length();
		sb.append(mUnfoldText);
		
		sb.setSpan(span, addIndex, sb.length(), Spanned.SPAN_POINT_MARK);
		
		if(!mAlignViewEdge) return sb;
		while (tmpLayout.getLineCount() <= mFoldLineMax) {
			sb.replace(addIndex, addIndex, " ");
			addIndex++ ;
		}
		sb.delete(addIndex-1, addIndex);
		return sb;
	}

	private void setOnClickListener(boolean paramBoolean) {
		if (paramBoolean) {
			setOnClickListener(this);
			return;
		}
		setOnClickListener(null);
	}

	public boolean getFoldStatus() {
		return mIsfolded;
	}

	@Override
	public void onClick(View view) {
		if(mIsClickSpan||!mClickToFold){
			mIsClickSpan = false;
			return;
		}
		if (mIsfolded) {
			//已经折叠了,更改状态
			mIsfolded = false;
			setText(mainText, TextView.BufferType.NORMAL);
			if (mListener != null) mListener.onFolding(this, false);
		} else {
			mIsfolded = true;
			setText(foldText(mainText));
			if (mListener != null) mListener.onFolding(this, true);
		}
	}

	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		if (!mIsfolded || mFoldLineMax <= 0)return;
		
		if(mainText == null || TextUtils.isEmpty(getText()))mainText = getText();
		if (mainText != null && (mainText instanceof Spanned)) {
			CharSequence dst = foldText(mainText);
			if (!TextUtils.equals(mainText, dst)
					|| (mListener != null && mListener.onFolding(this, true))) {
				setText(dst, TextView.BufferType.SPANNABLE);
			}
		}
	}

	public void setClickToFold(boolean clickToFold) {
		mClickToFold = clickToFold;
	}

	public void setFoldStatus(boolean foldStatus) {
		if (this.mIsfolded != foldStatus) {
			this.mIsfolded = foldStatus;
			requestLayout();
			invalidate();
		}
	}

	public void setFoldText(String ellipseText, String unfoldText,
			boolean isAlignViewEdge) {
		this.mAlignViewEdge = isAlignViewEdge;
		if (ellipseText != null) {
			mEllipseText = ellipseText;
		}
		if (unfoldText != null) {
			mUnfoldText = unfoldText;
		}
	}

	public void setFolding(int foldLineMax, FoldingListener listener) {
		this.mFoldLineMax = foldLineMax;
		this.mListener = listener;
		if (mFoldLineMax > 0) {
			requestLayout();
			invalidate();
		}
	}

	public void setLinkColor(int paramInt) {
		this.mLinkColor = paramInt;
		invalidate();
	}

	@Override
	public void setText(CharSequence paramCharSequence,
			TextView.BufferType paramBufferType) {
		super.setText(paramCharSequence, paramBufferType);
		requestLayout();
	}

	public static abstract interface FoldingListener {
		public abstract boolean onFolding(FoldableTextView textView,
				boolean isfolded);
	}

	private class MoreClickSpan extends ClickableSpan {
		private final CharSequence mText;

		public MoreClickSpan(CharSequence text) {
			this.mText = text;
		}

		@Override
		public void onClick(View view) {
			if ((mListener != null)&& (!mListener.onFolding(FoldableTextView.this, false))) {
				return;
			}
			//不是折叠状态
			mIsfolded  = false;
			setText(mText, TextView.BufferType.NORMAL);
			mIsClickSpan  = true;
		}

		@Override
		public void updateDrawState(TextPaint textPaint) {
			if (mLinkColor == -1) {
				textPaint.setColor(textPaint.linkColor);
				return;
			}
			textPaint.setColor(mLinkColor);
		}
	}
}


attrs.xml

  <declare-styleable name="FoldableTextView">
        <attr name="mzTextEllipse" format="string" />
        <attr name="mzTextUnfold" format="string" />
        <attr name="mzMaxFoldLine" format="integer" />
        <attr name="mzUnfoldAlignViewEdge" format="boolean" />
        <attr name="mzClickToFold" format="boolean" />
        <attr name="mzLinkColor" format="color" />
        <attr name="mzIsFold" format="boolean" />
    </declare-styleable>




参考:http://android.blog.51cto.com/268543/384809/

                   http://blog.csdn.net/yuanyuan_186/article/details/41358597

                  JVM指令上

  JVM指令下

                 JVM指令集


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值