一、工具
个人比较喜欢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),但是不能靠这个引用来访问外部类的私有域。那么非静态内部类是如何大摇大摆的访问外部类的私有域的呢?
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