Android 自定义布局 性能问题 初探

大家在写android 代码的时候,基本上都使用过如下几种布局 RelativeLayout,LinearLayout, FrameLayout

但是很多时候 这几种布局 也无法满足我们的使用。于是我们会考虑用自定义布局,使用自定义布局会有几个优点

比如可以减少view的使用啊,让ui显示的更加有效率啊,以及实现一些原生控件无法实现的效果。

我们首先去github上 下载一个开源项目 https://github.com/lucasr/android-layout-samples

注意这个项目是基于android studio 结构的。你如果用Eclipse来导入是导入不成功的。

最近github上很多开源项目都开始支持android studio了。所以还是建议大家拥抱下谷歌的新ide。

然后这个项目的作者是http://lucasr.org/about/ 就是国外一个很牛逼的android 工程师,我们就以他

的开源项目以及博客 来感受一下 自定义布局的性能。

这个项目运行起来以后实际上就是仿照的twitter的一些效果。图片库用的是picasso。有兴趣的同学可以

去http://square.github.io/picasso/  这个地方看一下这个图片库。

然后我们来看第一个自定义ui  TweetCompositeView

 1 /*
 2  * Copyright (C) 2014 Lucas Rocha
 3  *
 4  * Licensed under the Apache License, Version 2.0 (the "License");
 5  * you may not use this file except in compliance with the License.
 6  * You may obtain a copy of the License at
 7  *
 8  *     http://www.apache.org/licenses/LICENSE-2.0
 9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.lucasr.layoutsamples.widget;
18
19 import android.content.Context;
20 import android.text.TextUtils;
21 import android.util.AttributeSet;
22 import android.view.LayoutInflater;
23 import android.view.View;
24 import android.widget.ImageView;
25 import android.widget.RelativeLayout;
26 import android.widget.TextView;
27
28 import org.lucasr.layoutsamples.adapter.Tweet;
29 import org.lucasr.layoutsamples.adapter.TweetPresenter;
30 import org.lucasr.layoutsamples.app.R;
31 import org.lucasr.layoutsamples.util.ImageUtils;
32
33 import java.util.EnumMap;
34 import java.util.EnumSet;
35
36 public class TweetCompositeView extends RelativeLayout implements TweetPresenter {
37     private final ImageView mProfileImage;
38     private final TextView mAuthorText;
39     private final TextView mMessageText;
40     private final ImageView mPostImage;
41     private final EnumMap<Action, ImageView> mActionIcons;
42
43     public TweetCompositeView(Context context, AttributeSet attrs) {
44         this(context, attrs, 0);
45     }
46
47     public TweetCompositeView(Context context, AttributeSet attrs, int defStyleAttr) {
48         super(context, attrs, defStyleAttr);
49
50         LayoutInflater.from(context).inflate(R.layout.tweet_composite_view, this, true);
51         mProfileImage = (ImageView) findViewById(R.id.profile_image);
52         mAuthorText = (TextView) findViewById(R.id.author_text);
53         mMessageText = (TextView) findViewById(R.id.message_text);
54         mPostImage = (ImageView) findViewById(R.id.post_image);
55
56         mActionIcons = new EnumMap(Action.class);
57         for (Action action : Action.values()) {
58             final ImageView icon;
59             switch (action) {
60                 case REPLY:
61                     icon = (ImageView) findViewById(R.id.reply_action);
62                     break;
63
64                 case RETWEET:
65                     icon = (ImageView) findViewById(R.id.retweet_action);
66                     break;
67
68                 case FAVOURITE:
69                     icon = (ImageView) findViewById(R.id.favourite_action);
70                     break;
71
72                 default:
73                     throw new IllegalArgumentException("Unrecognized tweet action");
74             }
75
76             mActionIcons.put(action, icon);
77         }
78     }
79
80     @Override
81     public boolean shouldDelayChildPressedState() {
82         return false;
83     }
84
85     @Override
86     public void update(Tweet tweet, EnumSet<UpdateFlags> flags) {
87         mAuthorText.setText(tweet.getAuthorName());
88         mMessageText.setText(tweet.getMessage());
89
90         final Context context = getContext();
91         ImageUtils.loadImage(context, mProfileImage, tweet.getProfileImageUrl(), flags);
92
93         final boolean hasPostImage = !TextUtils.isEmpty(tweet.getPostImageUrl());
94         mPostImage.setVisibility(hasPostImage ? View.VISIBLE : View.GONE);
95         if (hasPostImage) {
96             ImageUtils.loadImage(context, mPostImage, tweet.getPostImageUrl(), flags);
97         }
98     }
99 }

我们可以看一下这个自定义ui。实际上这个自定义ui非常简单,我们工作中也经常这样使用自定义ui。

他一般就是这么使用的:

1 继承一个layout。当然这个layout可以是相对布局 也可以是流布局

2 在构造函数里 inflate 我们的布局文件 同时初始化我们的自定义布局的子元素

3 增加一些对应的方法 来更新我们的元素 比如说 update 这个方法 就是来做这个工作的。

然后我们来看一下这个布局对应的布局文件

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <!--
 3   ~ Copyright (C) 2014 Lucas Rocha
 4   ~
 5   ~ Licensed under the Apache License, Version 2.0 (the "License");
 6   ~ you may not use this file except in compliance with the License.
 7   ~ You may obtain a copy of the License at
 8   ~
 9   ~     http://www.apache.org/licenses/LICENSE-2.0
10   ~
11   ~ Unless required by applicable law or agreed to in writing, software
12   ~ distributed under the License is distributed on an "AS IS" BASIS,
13   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   ~ See the License for the specific language governing permissions and
15   ~ limitations under the License.
16   -->
17
18 <merge xmlns:android="http://schemas.android.com/apk/res/android"
19     android:layout_width="match_parent"
20     android:layout_height="match_parent">
21
22     <ImageView
23         android:id="@+id/profile_image"
24         android:layout_width="@dimen/tweet_profile_image_size"
25         android:layout_height="@dimen/tweet_profile_image_size"
26         android:layout_marginRight="@dimen/tweet_content_margin"
27         android:scaleType="centerCrop"/>
28
29     <TextView
30         android:id="@+id/author_text"
31         android:layout_width="fill_parent"
32         android:layout_height="wrap_content"
33         android:layout_toRightOf="@id/profile_image"
34         android:layout_alignTop="@id/profile_image"
35         android:textColor="@color/tweet_author_text_color"
36         android:textSize="@dimen/tweet_author_text_size"
37         android:singleLine="true"/>
38
39     <TextView
40         android:id="@+id/message_text"
41         android:layout_width="fill_parent"
42         android:layout_height="wrap_content"
43         android:layout_below="@id/author_text"
44         android:layout_alignLeft="@id/author_text"
45         android:textColor="@color/tweet_message_text_color"
46         android:textSize="@dimen/tweet_message_text_size"/>
47
48     <ImageView
49         android:id="@+id/post_image"
50         android:layout_width="fill_parent"
51         android:layout_height="@dimen/tweet_post_image_height"
52         android:layout_below="@id/message_text"
53         android:layout_alignLeft="@id/message_text"
54         android:layout_marginTop="@dimen/tweet_content_margin"
55         android:scaleType="centerCrop"/>
56
57     <LinearLayout android:layout_width="fill_parent"
58         android:layout_height="wrap_content"
59         android:layout_below="@id/post_image"
60         android:layout_alignLeft="@id/message_text"
61         android:layout_marginTop="@dimen/tweet_content_margin"
62         android:orientation="horizontal">
63
64         <ImageView
65             android:id="@+id/reply_action"
66             android:layout_width="0dp"
67             android:layout_height="@dimen/tweet_icon_image_size"
68             android:layout_weight="1"
69             android:src="@drawable/tweet_reply"
70             android:scaleType="fitStart"/>
71
72         <ImageView
73             android:id="@+id/retweet_action"
74             android:layout_width="0dp"
75             android:layout_height="@dimen/tweet_icon_image_size"
76             android:layout_weight="1"
77             android:src="@drawable/tweet_retweet"
78             android:scaleType="fitStart"/>
79
80         <ImageView
81             android:id="@+id/favourite_action"
82             android:layout_width="0dp"
83             android:layout_height="@dimen/tweet_icon_image_size"
84             android:layout_weight="1"
85             android:src="@drawable/tweet_favourite"
86             android:scaleType="fitStart"/>
87
88     </LinearLayout>
89
90 </merge>

我们可以来看一下这个布局 其中包含了 LinearLayout 这个布局。 我们知道在android里面 linearlayout和relativelayout 是非常复杂的ui

这种viewgroup 会不断的检测子view的大小和布局位置。 所以实际上效率是有损失的。所以我们如果想更近一步的 优化我们的ui效率

我们要尽量避免使用这种高级的viewgroup

比如我们可以来看看这个view

  1 /*
  2  * Copyright (C) 2014 Lucas Rocha
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the "License");
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *     http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16
 17 package org.lucasr.layoutsamples.widget;
 18
 19 import android.content.Context;
 20 import android.text.TextUtils;
 21 import android.util.AttributeSet;
 22 import android.view.LayoutInflater;
 23 import android.view.View;
 24 import android.view.ViewGroup;
 25 import android.widget.ImageView;
 26 import android.widget.TextView;
 27
 28 import org.lucasr.layoutsamples.adapter.Tweet;
 29 import org.lucasr.layoutsamples.adapter.TweetPresenter;
 30 import org.lucasr.layoutsamples.app.R;
 31 import org.lucasr.layoutsamples.util.ImageUtils;
 32
 33 import java.util.EnumMap;
 34 import java.util.EnumSet;
 35
 36 public class TweetLayoutView extends ViewGroup implements TweetPresenter {
 37     private final ImageView mProfileImage;
 38     private final TextView mAuthorText;
 39     private final TextView mMessageText;
 40     private final ImageView mPostImage;
 41     private final EnumMap<Action, View> mActionIcons;
 42
 43     public TweetLayoutView(Context context, AttributeSet attrs) {
 44         this(context, attrs, 0);
 45     }
 46
 47     public TweetLayoutView(Context context, AttributeSet attrs, int defStyleAttr) {
 48         super(context, attrs, defStyleAttr);
 49
 50         LayoutInflater.from(context).inflate(R.layout.tweet_layout_view, this, true);
 51         mProfileImage = (ImageView) findViewById(R.id.profile_image);
 52         mAuthorText = (TextView) findViewById(R.id.author_text);
 53         mMessageText = (TextView) findViewById(R.id.message_text);
 54         mPostImage = (ImageView) findViewById(R.id.post_image);
 55
 56         mActionIcons = new EnumMap(Action.class);
 57         for (Action action : Action.values()) {
 58             final int viewId;
 59             switch (action) {
 60                 case REPLY:
 61                     viewId = R.id.reply_action;
 62                     break;
 63
 64                 case RETWEET:
 65                     viewId = R.id.retweet_action;
 66                     break;
 67
 68                 case FAVOURITE:
 69                     viewId = R.id.favourite_action;
 70                     break;
 71
 72                 default:
 73                     throw new IllegalArgumentException("Unrecognized tweet action");
 74             }
 75
 76             mActionIcons.put(action, findViewById(viewId));
 77         }
 78     }
 79
 80     private void layoutView(View view, int left, int top, int width, int height) {
 81         MarginLayoutParams margins = (MarginLayoutParams) view.getLayoutParams();
 82         final int leftWithMargins = left + margins.leftMargin;
 83         final int topWithMargins = top + margins.topMargin;
 84
 85         view.layout(leftWithMargins, topWithMargins,
 86                     leftWithMargins + width, topWithMargins + height);
 87     }
 88
 89     private int getWidthWithMargins(View child) {
 90         final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
 91         return child.getWidth() + lp.leftMargin + lp.rightMargin;
 92     }
 93
 94     private int getHeightWithMargins(View child) {
 95         final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
 96         return child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
 97     }
 98
 99     private int getMeasuredWidthWithMargins(View child) {
100         final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
101         return child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
102     }
103
104     private int getMeasuredHeightWithMargins(View child) {
105         final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
106         return child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
107     }
108
109     @Override
110     public boolean shouldDelayChildPressedState() {
111         return false;
112     }
113
114     @Override
115     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
116         final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
117
118         int widthUsed = 0;
119         int heightUsed = 0;
120
121         measureChildWithMargins(mProfileImage,
122                                 widthMeasureSpec, widthUsed,
123                                 heightMeasureSpec, heightUsed);
124         widthUsed += getMeasuredWidthWithMargins(mProfileImage);
125
126         measureChildWithMargins(mAuthorText,
127                                 widthMeasureSpec, widthUsed,
128                                 heightMeasureSpec, heightUsed);
129         heightUsed += getMeasuredHeightWithMargins(mAuthorText);
130
131         measureChildWithMargins(mMessageText,
132                                 widthMeasureSpec, widthUsed,
133                                 heightMeasureSpec, heightUsed);
134         heightUsed += getMeasuredHeightWithMargins(mMessageText);
135
136         if (mPostImage.getVisibility() != View.GONE) {
137             measureChildWithMargins(mPostImage,
138                                     widthMeasureSpec, widthUsed,
139                                     heightMeasureSpec, heightUsed);
140             heightUsed += getMeasuredHeightWithMargins(mPostImage);
141         }
142
143         int maxIconHeight = 0;
144         for (Action action : Action.values()) {
145             final View iconView = mActionIcons.get(action);
146             measureChildWithMargins(iconView,
147                                     widthMeasureSpec, widthUsed,
148                                     heightMeasureSpec, heightUsed);
149
150             final int height = getMeasuredHeightWithMargins(iconView);
151             if (height > maxIconHeight) {
152                 maxIconHeight = height;
153             }
154
155             widthUsed += getMeasuredWidthWithMargins(iconView);
156         }
157         heightUsed += maxIconHeight;
158
159         int heightSize = heightUsed + getPaddingTop() + getPaddingBottom();
160         setMeasuredDimension(widthSize, heightSize);
161     }
162
163     @Override
164     protected void onLayout(boolean changed, int l, int t, int r, int b) {
165         final int paddingLeft = getPaddingLeft();
166         final int paddingTop = getPaddingTop();
167
168         int currentTop = paddingTop;
169
170         layoutView(mProfileImage, paddingLeft, currentTop,
171                    mProfileImage.getMeasuredWidth(),
172                    mProfileImage.getMeasuredHeight());
173
174         final int contentLeft = getWidthWithMargins(mProfileImage) + paddingLeft;
175         final int contentWidth = r - l - contentLeft - getPaddingRight();
176
177         layoutView(mAuthorText, contentLeft, currentTop,
178                    contentWidth, mAuthorText.getMeasuredHeight());
179         currentTop += getHeightWithMargins(mAuthorText);
180
181         layoutView(mMessageText, contentLeft, currentTop,
182                 contentWidth, mMessageText.getMeasuredHeight());
183         currentTop += getHeightWithMargins(mMessageText);
184
185         if (mPostImage.getVisibility() != View.GONE) {
186             layoutView(mPostImage, contentLeft, currentTop,
187                        contentWidth, mPostImage.getMeasuredHeight());
188
189             currentTop += getHeightWithMargins(mPostImage);
190         }
191
192         final int iconsWidth = contentWidth / mActionIcons.size();
193         int iconsLeft = contentLeft;
194
195         for (Action action : Action.values()) {
196             final View icon = mActionIcons.get(action);
197
198             layoutView(icon, iconsLeft, currentTop,
199                        iconsWidth, icon.getMeasuredHeight());
200             iconsLeft += iconsWidth;
201         }
202     }
203
204     @Override
205     public LayoutParams generateLayoutParams(AttributeSet attrs) {
206         return new MarginLayoutParams(getContext(), attrs);
207     }
208
209     @Override
210     protected LayoutParams generateDefaultLayoutParams() {
211         return new MarginLayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
212     }
213
214     @Override
215     public void update(Tweet tweet, EnumSet<UpdateFlags> flags) {
216         mAuthorText.setText(tweet.getAuthorName());
217         mMessageText.setText(tweet.getMessage());
218
219         final Context context = getContext();
220         ImageUtils.loadImage(context, mProfileImage, tweet.getProfileImageUrl(), flags);
221
222         final boolean hasPostImage = !TextUtils.isEmpty(tweet.getPostImageUrl());
223         mPostImage.setVisibility(hasPostImage ? View.VISIBLE : View.GONE);
224         if (hasPostImage) {
225             ImageUtils.loadImage(context, mPostImage, tweet.getPostImageUrl(), flags);
226         }
227     }
228 }

然后看看他的布局文件

<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright (C) 2014 Lucas Rocha
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~     http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  -->

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/profile_image"
        android:layout_width="@dimen/tweet_profile_image_size"
        android:layout_height="@dimen/tweet_profile_image_size"
        android:layout_marginRight="@dimen/tweet_content_margin"
        android:scaleType="centerCrop"/>

    <TextView
        android:id="@+id/author_text"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textColor="@color/tweet_author_text_color"
        android:textSize="@dimen/tweet_author_text_size"
        android:singleLine="true"/>

    <TextView
        android:id="@+id/message_text"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="@dimen/tweet_content_margin"
        android:textColor="@color/tweet_message_text_color"
        android:textSize="@dimen/tweet_message_text_size"/>

    <ImageView
        android:id="@+id/post_image"
        android:layout_width="fill_parent"
        android:layout_height="@dimen/tweet_post_image_height"
        android:layout_marginBottom="@dimen/tweet_content_margin"
        android:scaleType="centerCrop"/>

    <ImageView
        android:id="@+id/reply_action"
        android:layout_width="@dimen/tweet_icon_image_size"
        android:layout_height="@dimen/tweet_icon_image_size"
        android:src="@drawable/tweet_reply"
        android:scaleType="fitStart"/>

    <ImageView
        android:id="@+id/retweet_action"
        android:layout_width="@dimen/tweet_icon_image_size"
        android:layout_height="@dimen/tweet_icon_image_size"
        android:src="@drawable/tweet_retweet"
        android:scaleType="fitStart"/>

    <ImageView
        android:id="@+id/favourite_action"
        android:layout_width="@dimen/tweet_icon_image_size"
        android:layout_height="@dimen/tweet_icon_image_size"
        android:src="@drawable/tweet_favourite"
        android:scaleType="fitStart"/>

</merge>

  

看一下我们就会发现,TweetLayoutView 是通过 onMeasure onlayout 自己来决定子布局的大小和位置的

完全跟linearlayout和relativelayout 没有任何关系。这样性能上就有极大的提高。

当然我们不可能自己实现 所有的layout对吧,不然的话 我们就都去谷歌了。。。。哈哈。

但是可以有选择的把你app里 ui最复杂的地方 选择性的优化他。提高 ui渲染的效率。

最后我们看一下前面这个TweetLayoutView  这个布局实际上还不是最优解。

因为里面有很多系统自带的imageview 和textview。

我们可以打开一下设置--开发者选项-显示布局边界 这个功能

这个功能可以把你当前app的 布局边界全部标示出来

我们可以打开android 版的gmail 随便点击个列表。

可以看一下他们listview里的每个item 布局边界都是在外面。里面没有任何布局边界。

所以可以得知gmail的listview里的 item 是自己重写的一整个view 里面没有使用

任何系统自带的textview 或者是imageview 之类的。

这样就是ui终极进化了。。。。。。

当然这么做 工作量很多,而且很多地方需要考虑。比如你自己画文本是简单了,效率是提高了

但是textview 的文本截断呢?你能做么?imageview里的图片缩放呢?你能做么?

所以我们在自定义布局的时候 除了考虑ui实现的效率,我们还需要着重考虑实现的难度,和技术上的风险。

个人感觉只需要修改你app最卡顿的地方的布局 即可。尤其是listview viewpager里面的item

这一般在低端手机上 确实会出现卡帧的现象。其他地方看情况修改。

最后 https://github.com/lucasr/android-layout-samples 这个项目大家没事可以多看看,

有很多值得学习的地方。

时间: 2024-10-25 16:33:34

Android 自定义布局 性能问题 初探的相关文章

Android自定义布局

1.首先新建属性文件 <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="topbar"> <attr name="title" format="string"/> <attr name="titleTextSize" format=&

Android自定义布局的三种实现方式

在毕设项目中多处用到自定义布局,一直打算总结一下自定义布局的实现方式,今天就来总结一下吧.在此之前学习了郭霖大神博客上面关于自定义View的几篇博文,感觉受益良多,本文中就参考了其中的一些内容. 总结来说,自定义布局的实现有三种方式,分别是:组合控件.自绘控件和继承控件.下面将分别对这三种方式进行介绍. (一)组合控件 组合控件,顾名思义就是将一些小的控件组合起来形成一个新的控件,这些小的控件多是系统自带的控件.比如很多应用中普遍使用的标题栏控件,其实用的就是组合控件,那么下面将通过实现一个简单

android自定义布局中的平滑移动

在android应用程序的开发过程中,相信我们很多人都想把应用的交互做的比较绚丽,比如让界面切换平滑的滚动,还有热度灰常高的伪3D等界面效果,通常情况下,系统提供的应用在特效这方面只能为我们提供简单的动画接口,所以要想实现比较酷炫的效果还是要自己去开发布局控件(即所谓的自定义View.ViewGroup).小弟也经常做一些自定义的控件,最近工作比较清闲,所以便将自己对自定义布局控件的一些心得写出来,权当是自己的学习笔记了,各位高手看到了可以忽略 .下面就我最近工作中遇到的一个自定义控件开发做一些

android自定义布局的使用!

继承viewGroup: 自定义控件的左边距:右边距:上边距,下边距: java 代码: package com.example.customview1406_04myviwegroup; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; /** * 自定义的一种布局,指定每个子控件如何显示 * * @

深入解析Android的自定义布局

原文链接:http://greenrobot.me/devpost/android-custom-layout/ 写在前面的话: 这篇文章是前Firefox Android工程师(现在跳槽去Facebook了)Lucas Rocha所写,文中对Android中常用的四种自定义布局方案进行了很好地分析,并结合这四种Android自定义布局方案所写的示例项目讲解了它们各自的优劣以及四种方案之间的比较.看完这篇文章,也让我对Android 自定义布局有了进一步的了解,于是趁着兴头,我把它翻译成中文,原

android 自定义

初级: 1.Android自定义View之一:初探实例 ——> onDraw 2.getwidth和getmeasuredwidth的区别以及两者的使用场景 3.Android 自定义View (一)——>onMesure  MeasureSpec 4.Android LayoutInflater原理分析,带你一步步深入了解View(一) 5.Android视图绘制流程完全解析,带你一步步深入了解View(二) 6.Android视图状态及重绘流程分析,带你一步步深入了解View(三) 7.A

深入解析_Android的自定义布局

前言: 这篇文章是前Firefox Android工程师(现在跳槽去Facebook了) Lucas Rocha所写,文中对Android中常用的四种自定义布局方案进行了很好地分析,并结合这四种Android自定义布局方案所写的示例项目讲解了它们各自的优劣以及四种方案之间的比较.看完这篇文章,也让我对Android 自定义布局有了进一步的了解,于是趁着兴头,我把它翻译成中文,原文链接在此. 只要你写过Android程序,你肯定使用过Android平台内建的几个布局——RelativeLayout

Android开发学习之路--UI之自定义布局和控件

新的一年已经开始了,今天已经是初二了,两天没有学习了,还是要来继续学习下.一般手机的title都是actionbar,就像iphone一样可以后退,可以编辑.这里自定义布局就来实现下这个功能,首先准备下三张图片,一张用来当作背景,两张分别表示后退和编辑.新建工程UICostomViewsTest,然后自动创建工程后,新建title.xml,编写代码如下: <?xml version="1.0" encoding="utf-8"?> <LinearL

Android:创建可穿戴应用 - 自定义布局

创建自定义布局(Creating Custom Layouts) 本文将介绍如何创建自定义通知以及使用可穿戴UI库来创建自定义布局你同时还需要了解可穿戴设计准则(Wear Design Principles)除了屏幕尺寸和瞬读能力(Glance ability)外,为可穿戴应用创建布局大体和普通手机一样. 创建自定义通知(Custom Notifications) 通常,你应该在手机应用上创建通知然后自动同步到可穿戴应用.这让你只需要构建通知一次,就可以呈现于多种设备(不只是可穿戴,最终还包括汽