Skip to content

Commit

Permalink
Merge branch 'master' of github.com:bboyfeiyu/android-tech-frontier
Browse files Browse the repository at this point in the history
  • Loading branch information
hehonghui committed Sep 14, 2015
2 parents 040a518 + 763c86a commit a111d08
Show file tree
Hide file tree
Showing 13 changed files with 2,048 additions and 2 deletions.
7 changes: 5 additions & 2 deletions issue-23/Android-MVVM模式.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

---
> * 原文链接 : [MVVM on Android using the Data Binding Library](http://stablekernel.com/blog/mvvm-on-android-using-the-data-binding-library/)
> * 译者 : [shenyansycn](https://github.com/shenyansycn)
> * 校对 :
* 原文作者 : [Ross Hambrick](http://stablekernel.com/)
* 译文出自 : [开发技术前线 www.devtf.cn。未经允许,不得转载!](http://www.devtf.cn)
* 译者 : [shenyansycn](https://github.com/shenyansycn)
* 校对者: [chaossss](https://github.com/chaossss)
* 状态 : 完成

Google 2015开发者大会终于来了,其中只有一个开发工具真的让我兴奋。我们看到了一系列的改善,例如Android M和以用户为中心的特性,Android Studio支持NDK(C/C++),矢量图,heap分析,改进的主题和layout编辑器,Gradle性能改善,等等。我高兴的是我们终于有了一个[Design Support Library][3] 可以实现[Material Design UI patterns][4]。但大多数这些都已经被其他社区工具和库实现。

Expand Down
68 changes: 68 additions & 0 deletions issue-23/TextView的TextLayout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
多文本布局
---
> * 原文链接 : [Multiple Text Layout](https://sriramramani.wordpress.com/2014/08/14/multiple-text-layout/)
* 原文作者 : [Sriram Ramani](https://sriramramani.wordpress.com/author/sriramramani/)
* [译文出自 : 开发技术前线 www.devtf.cn](http://www.devtf.cn)
* 译者 : [dengshiwei](https://github.com/dengshiwei)
* 校对者 :[chaossss](https://github.com/chaossss)
* 状态 : 已完成

在Android中开发UI的最基本的单元就是一个视图(View)。但是,如果我们仔细观察,视图(View)是一个与用户交互的UI控件。它包含Drawables和text [Layouts](http://developer.android.com/reference/android/text/Layout.html)。我们随处可见drawables —— 视图(View)的背景(backgroud)。TextView同样由drawables组成。然而,TextView只有一个布局(layout)。在一个View/TextView中是否可能有多于一个的(布局)layout?
![FIRST](https://sriramramani.files.wordpress.com/2014/08/badges.png)

让我们看一个例子。我们有一个简单的ListView,它每行中包含一个image、text以及一些sub-text。由于TextView中显示默认只有一个文本布局(text Layout),我们需要一个包含2个或3个视图(views)的LinearLayout来实现这种布局(layout)。如果TextView能够容纳一个以上的布局(layout)将会是什么(情况)?即使它(TextView)能够容纳和绘制,它也是一个可以创建和绘制的私有变量(private variable)。我们如何才能够让TextView的原始布局(original layout)实现这种布局(layout)呢?

如果我们仔细看TextView的onMeasure方法,布局(layout)的可用width占据着compound drawables所占用的空间。如果我们让TextView占有compound drawable右侧的大部分,这个布局(layout)将会包含自己更多。现在空间划分出来了,我们可以在这个空间绘制(draw)布局。

private Layout mSubTextLayout;

@Override
public int getCompoundPaddingRight() {
// Assumption: the layout has only one line.
return super.getCompoundPaddingRight() + mSubTextLayout.getLineWidth(0);
}

现在我们需要为sub-text制造一个布局(layout)并绘制它。理想情况下,在onMeasure()方法中去创建一个对象是不好的。但是如果我们注意何时以及如何创建布局(layouts),我们不需要担心这个限制。
我们可以创建什么种类的布局(layouts)呢?TextView允许创建BoringLayout,BoringLayout是一个 StaticLayout或DynamicLayout。BoringLayout用于文本(text)如果只有一行的情况下。StaticLayout 用于multi-line的布局(layouts),它创建后不能改变。DynamicLayout 用于可编辑的文本(editable text),如同EditText。

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);

// Create a layout for sub-text.
mSubTextLayout = new StaticLayout(
mSubText,
mPaint,
width,
Alignment.ALIGN_NORMAL,
1.0f,
0.0f,
true);

// TextView doesn't know about mSubTextLayout.
// It calculates the space using compound drawables' sizes.
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

此处的mPaint包含对于sub-text的所有属性,例如字体颜色(text-color)、阴影(shadow)、字体大小(text-size)等等。这些决定用于text layout的大小。

@Override
public void onDraw(Canvas canvas) {
// Do the default draw.
super.onDraw(canvas);

// Calculate the place to show the sub-text
// using the padding, available width, height and
// the sub-text width and height.
// Note: The 'right' padding to use here is 'super.getCompoundPaddingRight()'
// as we have faked the actual value.

// Draw the sub-text.
mLayout.draw(canvas);
}

但是,我们不能只使用Spannable文本(text),好吧!如果这个名字确实很长,并且已经形成多行或者需要ellipsized。

通过这样,我们使用同一个TextView可以绘制两个布局(layouts),同时也帮助我们移除了2个视图(Views)! Happy hacking!

P.S: 这些图标来自: [http://www.tutorial9.net/downloads/108-mono-icons-huge-set-of-minimal-icons/](http://www.tutorial9.net/downloads/108-mono-icons-huge-set-of-minimal-icons/)
115 changes: 115 additions & 0 deletions issue-24/Android LayerDrawable 和 Drawable.Callback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#Android LayerDrawable 和 Drawable.Callback

> * 原文链接 : [Android LayerDrawable and Drawable.Callback](http://www.roman10.net/android-layerdrawable-and-drawable-callback/)
* 原文作者 : [Liu Feipeng](www.roman10.net)
* 译文出自 : [开发技术前线 www.devtf.cn。未经允许,不得转载!](www.devtf.cn)
* 译者 : [Desmond1121](https://github.com/desmond1121)
* 校对者:[bboyfeiyu](https://github.com/bboyfeiyu)

`LayerDrawable`是一个特殊的`Drawable`,它内部保持着一个`Drawable`数组,其中每一个`Drawable`都是视图中的一层。如果你不了解`LayerDrawable`的机制,当程序出了问题后是很难去找到bug在哪里的。我发这些文章就是为了分享在使用`LayerDrawable``Drawable.Callback`时可能出现的一个bug。

##Callback调用链

`LayerDrawable`中,每层视图(`Drawable`)都会将`LayerDrawable`注册为它的`Drawable.Callback`。这允许`Drawable`能够在需要重绘自己的时候告知`LayerDrawable`重绘它。我们可以在下面这个`Callback.invalidateSelf()`函数中看到是由注册callback端(在此处为`LayerDrawable`)来执行`invalidateDrawable(Drawable drawable)`的。

```java
public void invalidateSelf() {
/* 获取注册的Callback实例,如果无则返回null。 */
final Callback callback = getCallback();
if (callback != null) {
callback.invalidateDrawable(this);
}
}
```

我们知道`View`是实现了`Drawable.Callback`接口的,所以当图片需要重绘的时候就能够告知`View`。如果我们把`View`的背景图片设置成了`LayerDrawable`,在`Drawable`需要更新的时候callback的调用将有一个传递的过程,首先会调用注册的`LayerDrawable``invalidateDrawable(Drawable drawable)`方法,`LayerDrawable`又会调用`View``invalidateDrawable(Drawable drawable)`方法。如下图所示:

![Alt text](http://img.blog.csdn.net/20150818142917049)

##View改变背景时移除原背景Callback

`View``setBackgroundDrawable(Drawable background)`中有这么一段代码:

```java
if (mBackground != null) {
mBackground.setCallback(null);
unscheduleDrawable(mBackground);
}

if (background != null) {
background.setCallback(this);
}
```

我们可以看出:当`View`改变背景时将会**无条件将原背景(如果原背景是Drawable的话)的`Drawable.Callback`设置为`null`**

##什么情况下会出现Bug?

有了上面这些知识,我们可以通过下面这个步骤产生一个Bug:

> 1.`Drawable`**A **设置成`View`**V**的背景。**现在A的callback指向V**
2. 将A设置成`LayerDrawable` **L**中的一层。**现在A的callback指向L**
3. 现在为**V**设置另一个背景,**V会把原背景(A)的callback强制设置成null,破坏了A与L之间的联系。**
4. **BUG出现了**:更新`Drawable`**A**不会让**L**更新了。


解决方法就是在更新**V**的背景之后再创造`LayerDrawable`**L**。Bug发生与解决的例子可以在[这里](https://github.com/roman10/blog-android-source-code/tree/master/LayerDrawableCallback)下载。

为了方便看官,我也贴了一部分关键代码到这边来,你可以通过注释理解这段代码。

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn1 = (Button) findViewById(R.id.button1);
btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 1. 将 launcherIconDrawable.callback 赋值给 actionBar
actionBar.setBackgroundDrawable(launcherIconDrawable);
animateActionBarWorking();
}
});
Button btn2 = (Button) findViewById(R.id.button2);
btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 1. 将 launcherIconDrawable.callback 赋值给 actionBar
actionBar.setBackgroundDrawable(launcherIconDrawable);
animateActionBarNotWorking();
}
});
actionBar = getSupportActionBar();
launcherIconDrawable = getResources().getDrawable(R.drawable.launcher_repeat);
colorLayer = new ColorDrawable(Color.rgb(0, 255, 0));
actionBar.setBackgroundDrawable(colorLayer);
}

/* 这个函数运行后ActionBar不会得到更新。 */
private void animateActionBarNotWorking() {
Drawable[] layers = new Drawable[] { colorLayer, launcherIconDrawable };
LayerDrawable layerDrawable = new LayerDrawable(layers);
actionBar.setBackgroundDrawable(layerDrawable);
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 255);
valueAnimator.setDuration(1000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 4. Updates launcherIconDrawable will not trigger action bar background to update
// as launcherIconDrawable.callback is null
launcherIconDrawable.setAlpha((Integer) animation.getAnimatedValue());
}
});
valueAnimator.start();
}

/* 由于先移除了launcherIconDrawable与ActionBar的联系,这个函数运行后会让ActionBar得到更新。
private void animateActionBarWorking() {
actionBar.setBackgroundDrawable(null);
animateActionBarNotWorking();
}

参考文章:

[LayerDrawable - Android Developers](http://developer.android.com/reference/android/graphics/drawable/LayerDrawable.html)
Loading

0 comments on commit a111d08

Please sign in to comment.