您的位置:首页 > 移动开发 > Android开发

android逆向分析之smali练习

2015-07-15 13:19 841 查看
一、工具

第一章节分析了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指令集
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: