Android Layout Tricks #3: Optimize, Part 1

本文介绍了如何使用<merge/>标签来优化Android应用中的布局层级,减少不必要的视图层级,提高应用性能。通过实例展示了<merge/>的具体用法及注意事项。

http://www.curious-creature.org/2009/03/01/android-layout-tricks-3-optimize-part-1/


In the previous installment of Android Layout Tricks, I showed you how to use the <include /> tag in XML layout to reuse and share your layout code. I also mentioned the <merge /> and it’s now time to learn how to use it.

The <merge /> was created for the purpose of optimizing Android layouts by reducing the number of levels in view trees. It’s easier to understand the problem this tag solves by looking at an example. The following XML layout declares a layout that shows an image with its title on top of it. The structure is fairly simple; aFrameLayout is used to stack a TextView on top of an ImageView:

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

    <ImageView  
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent" 
    
        android:scaleType="center"
        android:src="@drawable/golden_gate" />
    
    <TextView
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_marginBottom="20dip"
        android:layout_gravity="center_horizontal|bottom"

        android:padding="12dip"
        
        android:background="#AA000000"
        android:textColor="#ffffffff"
        
        android:text="Golden Gate" />

</FrameLayout>

This layout renders nicely as we expected and nothing seems wrong with this layout:

A FrameLayout is used to overlay a title on top of an image

Things get more interesting when you inspect the result with HierarchyViewer. If you look closely at the resulting tree you will notice that the FrameLayout defined in our XML file (highlighted in blue below) is the sole child of another FrameLayout:

A layout with only one child of same dimensions can be removed

Since our FrameLayout has the same dimension as its parent, by the virtue of using the fill_parent constraints, and does not define any background, extra padding or a gravity, it is totally useless. We only made the UI more complex for no good reason. But how could we get rid of this FrameLayout? After all, XML documents require a root tag and tags in XML layouts always represent view instances.

That’s where the <merge /> tag comes in handy. When the LayoutInflater encounters this tag, it skips it and adds the <merge /> children to the <merge /> parent. Confused? Let’s rewrite our previous XML layout by replacing the FrameLayout with <merge />:

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

    <ImageView  
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent" 
    
        android:scaleType="center"
        android:src="@drawable/golden_gate" />
    
    <TextView
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        android:layout_marginBottom="20dip"
        android:layout_gravity="center_horizontal|bottom"

        android:padding="12dip"
        
        android:background="#AA000000"
        android:textColor="#ffffffff"
        
        android:text="Golden Gate" />

</merge>

With this new version, both the TextView and the ImageView will be added directly to the top-level FrameLayout. The result will be visually the same but the view hierarchy is simpler:

Optimized view hierarchy using the merge tag

Obviously, using <merge /> works in this case because the parent of an activity’s content view is always aFrameLayout. You could not apply this trick if your layout was using a LinearLayout as its root tag for instance. The <merge /> can be useful in other situations though. For instance, it works perfectly when combined with the<include /> tag. You can also use <merge /> when you create a custom composite view. Let’s see how we can use this tag to create a new view called OkCancelBar which simply shows two buttons with customizable labels. You can also download the complete source code of this example. Here is the XML used to display this custom view on top of an image:

<merge
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:okCancelBar="http://schemas.android.com/apk/res/com.example.android.merge">

    <ImageView  
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent" 
    
        android:scaleType="center"
        android:src="@drawable/golden_gate" />
    
    <com.example.android.merge.OkCancelBar
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:layout_gravity="bottom"

        android:paddingTop="8dip"
        android:gravity="center_horizontal"
        
        android:background="#AA000000"
        
        okCancelBar:okLabel="Save"
        okCancelBar:cancelLabel="Don't save" />

</merge>

This new layout produces the following result on a device:

Creating a custom view with the merge tag

The source code of OkCancelBar is very simple because the two buttons are defined in an external XML file, loaded using a LayoutInflate. As you can see in the following snippet, the XML layout R.layout.okcancelbaris inflated with the OkCancelBar as the parent:

public class OkCancelBar extends LinearLayout {
    public OkCancelBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOrientation(HORIZONTAL);
        setGravity(Gravity.CENTER);
        setWeightSum(1.0f);
        
        LayoutInflater.from(context).inflate(R.layout.okcancelbar, this, true);
        
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.OkCancelBar, 0, 0);
        
        String text = array.getString(R.styleable.OkCancelBar_okLabel);
        if (text == null) text = "Ok";
        ((Button) findViewById(R.id.okcancelbar_ok)).setText(text);
        
        text = array.getString(R.styleable.OkCancelBar_cancelLabel);
        if (text == null) text = "Cancel";
        ((Button) findViewById(R.id.okcancelbar_cancel)).setText(text);
        
        array.recycle();
    }
}

The two buttons are defined in the following XML layout. As you can see, we use the <merge /> tag to add the two buttons directly to the OkCancelBar. Each button is included from the same external XML layout file to make them easier to maintain; we simply override their id:

<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <include
        layout="@layout/okcancelbar_button"
        android:id="@+id/okcancelbar_ok" />
        
    <include
        layout="@layout/okcancelbar_button"
        android:id="@+id/okcancelbar_cancel" />
</merge>

We have created a flexible and easy to maintain custom view that generates an efficient view hierarchy:

The resulting hierarchy is simple and efficient

The <merge /> tag is extremely useful and can do wonders in your code. However, it suffers from a couple of limitation:

  • <merge /> can only be used as the root tag of an XML layout
  • When inflating a layout starting with a <merge />, you must specify a parent ViewGroup and you must setattachToRoot to true (see the documentation of the inflate() method)

In the next installment of Android Layout Tricks you will learn about ViewStub, a powerful variation of <include /> that can help you further optimize your layouts without sacrificing features.

Download the complete source code of this example.

21 Responses to “Android Layout Tricks #3: Optimize, Part 1”

  1. [...] Romain Guy: Android Layout Tricks #3: Optimize, Part 1 var disqus_title = “This Weeks Off-Site tutorials – W/C 2nd March 2009″;var disqus_message = ” Romain Guy: Android Layout Tricks #3: Optimize, Part 1 “;var disqus_developer = 0;View the forum thread. Add new comment [...]

  2. Sowmya  says:

    Hi Thanks for the tips….
    I have some query regarding hierarchy viewer..Can you help me on this?

    I connected my G1 phone and launched the hierarchy viewer. I then selected the device and clicked “Load View Hierarchy” option,the layout didnt get loaded but I got a “java.net.SocketException :Connection Reset”.
    Then i found that the view server was not running,while the device is connected. But it was running with the emulator
    HierarchyViewer worked fine with the emulator,but not with device.

    Alternately I tried the adb shell command “service” to start the view server on the device which too didnt work…

  3. Romain Guy  says:

    HierarchyViewer cannot work with production G1s. You need an engineering or custom build to make it work. This is for security reasons (otherwise HierarchyViewer would let you inspect all the UI data, including passwords, of any app of any phone.)

  4. Sowmya  says:

    Thanks for the help…

  5. Ddorid  says:

    really nice tutorial !

  6. [...] A ViewStub is a great compromise between ease of programming and efficiency. Instead of inflating views manually and adding them at runtime to your view hierarchy, simply use a ViewStub. It’s cheap and easy. The only drawback of ViewStub is that it currently does not support the tag. [...]

  7. [...] A ViewStub is a great compromise between ease of programming and efficiency. Instead of inflating views manually and adding them at runtime to your view hierarchy, simply use a ViewStub. It’s cheap and easy. The only drawback of ViewStub is that it currently does not support the <merge /> tag. [...]

  8. [...] A ViewStub is a great compromise between ease of programming and efficiency. Instead of inflating views manually and adding them at runtime to your view hierarchy, simply use a ViewStub. It’s cheap and easy. The only drawback of ViewStub is that it currently does not support the <merge /> tag. [...]

  9. Hey, nice tips. I’ll buy a glass of beer to that person from that chat who told me to visit your site :)

  10. [...] A ViewStub is a great compromise between ease of programming and efficiency. Instead of inflating views manually and adding them at runtime to your view hierarchy, simply use a ViewStub. It’s cheap and easy. The only drawback of ViewStub is that it currently does not support the <merge /> tag. [...]

  11. Xylenz  says:

    I’m confused [android newb alert]. The second xml snippet is not in MergeLayout.zip. Is that supposed to be okcancelbar.xml? (It would really help if you put the file names on the code snippets so we know what we are looking at.) If so then I get the error:

    No resource found that matches the given name (at ‘layout’ with value ‘@layout/okcancelbar_button’).

    for both of those resources. R.layout defines main, okcancelbar and okcancelbar_button. Dont those all have to exist as xml files in res/layout? There is only a main.xml. Is something missing?

  12. Abhijit Khadilkar  says:

    Hi the code given by you is not working properly .
    it throws errors.

  13. Mark  says:

    Would be a nice tutorial if it was complete! As Xylenz noted, two .xml files are missing: okcancelbar and okcancelbar_button. Also, the zip includes R.java, but this file should be auto-generated, not included in the project manually.

    While these are simple items to address by an experienced Android developer, they’re serious errors for a tutorial intended to be useful to people that don’t know much about Android development. I’m assuming such a basic tutorial is not intended for the experienced Android developer.

  14. Michael Lam  says:

    i tried to run your code on eclipse, but got these build errors:

    Description Resource Path Location Type
    error: No resource identifier found for attribute ‘cancelLabel’ in package ‘com.example.android.merge’ main.xml /MegeActivity/res/layout line 14 Android AAPT Problem
    error: No resource identifier found for attribute ‘cancelLabel’ in package ‘com.example.android.merge’ main.xml /MegeActivity/res/layout line 14 Android AAPT Problem
    error: No resource identifier found for attribute ‘okLabel’ in package ‘com.example.android.merge’ main.xml /MegeActivity/res/layout line 14 Android AAPT Problem

    can someone help me?

  15. Ponder Muse  says:

    Hi,

    I am new to android development and I have downloaded the above example’s zip file to try out. However, as others have already mentioned, the zip file is missing a couple of files.

    Could somebody please post these missing files so that the example can be tried out by Android noobs such as me?

    Thank you in advance for your troubles.

    Regards,
    PM.

  16. [...] Android layout tricks it’s another article where you can find the necessary details that can help you to work with FrameLayout. [...]

  17. Fahad  says:

    Dude, u are awesome . Thanks for the simple concept. I was looking for this tiny detail all over and it was just so annoying.
    Thanks you so much.

    Keep up the good work !!!

  18. Andrew  says:

    I’m having the same problem as some of the users above. Some files are missing from the sample. Any plans to remedy this? I am new to Android, and the fix is not immediately clear.

    Thanks

  19. John  says:

    Totally agree with comments on the xml. There appear to be cross-references between two xml files which are un-named. I’ve tried a number of permutations without success. Also the deprecated references to R.stylable. Help us out Romain – please.

  20. This was a good post.. This was my 1st time to this blog. Appreciate you for sharing . I have to subscribe to this website. I was a boat repairman for five years. Our do-it-yourself tip of the day is this: Please do not make an attempt a hard repair job without a professional. That will cost you additional money in the end. Thank you once again…..

  21. There are definitely plenty of particulars like that to take into consideration. That could be a nice point to deliver up. I offer the ideas above as basic inspiration however clearly there are questions like the one you deliver up the place a very powerful factor will probably be working in trustworthy good faith. I don?t know if finest practices have emerged around things like that, but I am certain that your job is clearly identified as a good game. Both boys and girls feel the affect of only a moment抯 pleasure, for the remainder of their lives.


import MDAnalysis as mda from MDAnalysis.analysis import density import numpy as np import matplotlib.pyplot as plt import pandas as pd import seaborn as sns #拟合曲线 from scipy import stats from MDAnalysis.analysis import lineardensity as lin from MDAnalysis.analysis import density from MDAnalysis.core.topologyattrs import Charges import mdtraj as md from sympy.physics.vector import gradient from scipy.optimize import curve_fit from scipy.signal import medfilt def vectorized_sliding_std(arr): padded = np.pad(arr, (1, 1), mode=&#39;wrap&#39;)#wrap 周期性条件 # 创建滑动窗口矩阵 windows = np.vstack([ padded[:-2], # i-1 padded[1:-1], # i padded[2:] # i+1 ]) # 一次性计算所有标准差 return np.std(windows, axis=0, ddof=1) def get_threshold_indices(arr, threshold): """ 获取一维数组中大于和小于阈值的元素索引 参数: arr (np.ndarray): 输入的一维数组 threshold (float): 分割阈值 返回: tuple: (greater_indices, less_indices) 分别表示大于阈值和小于阈值的索引数组 """ # 转换为NumPy数组确保操作兼容性 arr = np.asarray(arr) # 获取大于阈值的索引 greater_indices = np.where(arr > threshold)[0] # 获取小于阈值的索引 less_indices = np.where(arr < threshold)[0] return greater_indices, less_indices # 定义拟合函数形式(示例:对数函数) def log_func(x, a, b): return a * np.log(x) + b u= mda.Universe(&#39;E:\\data &mdash;&mdash;in\\两相共存2100\\50w.lmp&#39;,format=&#39;DATA&#39;,atom_style=&#39;id type x y z&#39;) u.load_new(&#39;E:\\data &mdash;&mdash;in\\数据分析\\1.trj&#39;,format=&#39;LammpsDump&#39;) u.add_TopologyAttr(Charges([0.0]*len(u.atoms)))#添加零电荷方便linedensity的计算 fe_mass=55.845 pt_mass=195.084 s_Pt=np.array([0])#每一帧pt在固相中的个数的数组 s_Fe=np.array([]) l_Pt= np.array([0]) l_Fe= np.array([]) frame=np.array([0]) s_Pt_parition=np.array([]) l_Pt_parition=np.array([]) D=np.array([]) for i in range(0,len(u.trajectory)): print(f&#39;====当前为第{i}帧====&#39;) u.trajectory[i] density = lin.LinearDensity(u.atoms, grouping=&#39;atoms&#39;, binsize=1, axis="y", density_type=(&#39;mass&#39;)).run( start=i, stop=i + 1) # 计算当前帧的Y轴线密度 #print(density.nbins) #print(density.results.y.mass_density) #print(density.results.y.hist_bin_edges) std=vectorized_sliding_std(density.results[&#39;y&#39;][&#39;mass_density&#39;]) #print(len(std)) #threshold=input("请输入阈值:") threshold=np.mean(std) solid_bin_idx, liquid_bin_idx =get_threshold_indices(std, threshold)#通过阈值判断相,返还盒子的索引 #print( solid_bin_idx,liquid_bin_idx) frame=np.append(frame,i) solid_bin_pt_number=np.array([0]) solid_bin_Fe_number=np.array([]) for i in solid_bin_idx: bin_right_coord=density.results.y.hist_bin_edges[i] bin_left_coord=density.results.y.hist_bin_edges[i-1] solid_bin_atoms = u.select_atoms(f"prop y < {bin_right_coord} and prop y >= {bin_left_coord} and type 2") solid_bin_Fe_atoms=u.select_atoms(f"prop y < {bin_right_coord} and prop y >= {bin_left_coord} and type 1") solid_bin_pt_number=np.append(solid_bin_pt_number, len(solid_bin_atoms)) solid_bin_Fe_number=np.append(solid_bin_Fe_number, len(solid_bin_Fe_atoms)) thisframe_s_Fe_num=np.sum(solid_bin_Fe_number) #print(thisframe_s_Fe_num) thisframe_s_Pt_num=np.sum(solid_bin_pt_number)#每一个盒子中的pt原子数 thisframe_s_pt_C_mol=thisframe_s_Pt_num/(thisframe_s_Pt_num+thisframe_s_Fe_num) #print(solid_bin_pt_number) this_frame_s_M_Fe=thisframe_s_Fe_num*fe_mass this_frame_s_M_Pt=thisframe_s_Pt_num*pt_mass s_Pt=np.append(s_Pt,thisframe_s_Pt_num)#将这一帧的数据存进s_Pt里 s_Fe=np.append(s_Fe,thisframe_s_Fe_num) this_frame_s_Pt_parition=(this_frame_s_M_Pt/(this_frame_s_M_Fe+this_frame_s_M_Pt))*100 #print(this_frame_s_Pt_parition) s_Pt_parition=np.append(s_Pt_parition,this_frame_s_Pt_parition) liquid_bin_pt_number = np.array([0]) liquid_bin_Fe_number = np.array([]) for i in liquid_bin_idx: bin_right_coord=density.results.y.hist_bin_edges[i] bin_left_coord=density.results.y.hist_bin_edges[i-1] liquid_bin_pt_atoms = u.select_atoms(f"prop y < {bin_right_coord} and prop y > {bin_left_coord} and type 2") liquid_bin_Fe_atoms=u.select_atoms(f"prop y < {bin_right_coord} and prop y > {bin_left_coord} and type 1") liquid_bin_pt_number=np.append(liquid_bin_pt_number, len(liquid_bin_pt_atoms)) liquid_bin_Fe_number=np.append(liquid_bin_Fe_number, len(liquid_bin_Fe_atoms)) thisframe_l_Fe_num=np.sum(liquid_bin_Fe_number) thisframe_l_Pt_num=np.sum(liquid_bin_pt_number) thisframe_l_Pt_C_mol=np.sum(liquid_bin_pt_number) this_frame_l_M_Fe=thisframe_l_Fe_num*fe_mass this_frame_l_M_Pt=thisframe_l_Pt_num*pt_mass l_Pt=np.append(l_Pt,thisframe_l_Pt_num) l_Fe=np.append(l_Fe,thisframe_l_Fe_num) this_frame_l_pt_parition=(this_frame_l_M_Pt/(this_frame_l_M_Fe+this_frame_l_M_Pt))*100 l_Pt_parition=np.append(l_Pt_parition,this_frame_l_pt_parition) D_this_frame=thisframe_s_pt_C_mol/thisframe_l_Pt_C_mol D=np.append(D,D_this_frame) s_Pt=np.delete(s_Pt,0) l_Pt=np.delete(l_Pt,0) frame=np.delete(frame,0) #print(len(frame)) print(s_Pt_parition) print(l_Pt_parition) #print(len(s_Pt)) &#39;&#39;&#39;x=frame y=s plt.bar(x, y, color=&#39;green&#39;, width=0.6) plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] plt.rcParams[&#39;axes.unicode_minus&#39;] = False plt.title(&#39;固体中pt随帧数变化的柱形图&#39;) plt.grid(True, linestyle=&#39;--&#39;, alpha=0.7)#添加网格 plt.tight_layout() # 自动调整布局 plt.show() &#39;&#39;&#39; #固液两相所有帧pt的频率分布直方图 s_flun=pd.Series(s_Pt) s_eg=s_flun.hist(bins=8, density=True, alpha=0.5) plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] plt.rcParams[&#39;axes.unicode_minus&#39;] = False s_flun.plot(kind=&#39;density&#39;) s_eg.set_title(&#39;固体中pt数的频率分布直方图&#39;) #plt.show() l_flun=pd.Series(l_Pt) l_eg=l_flun.hist(bins=8, density=True, alpha=0.5) plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] plt.rcParams[&#39;axes.unicode_minus&#39;] = False l_flun.plot(kind=&#39;density&#39;) l_eg.set_title(&#39;固体(绿色)/液体(蓝色)中pt数的频率分布直方图&#39;) plt.savefig(&#39;hinst1.png&#39;, dpi=600, bbox_inches=&#39;tight&#39;) plt.show() #分配过程中pt在两相中的质量占比变化 plt.scatter(frame,s_Pt_parition,c=&#39;red&#39;,marker=&#39;o&#39;) plt.scatter(frame,l_Pt_parition,c=&#39;blue&#39;,marker=&#39;o&#39;) plt.legend(loc=&#39;center right&#39;, bbox_to_anchor=(0.98, 0.5)).set_visible(False) plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] plt.rcParams[&#39;axes.unicode_minus&#39;] = False #拟合趋势曲线 y_data=medfilt(l_Pt_parition, kernel_size=5) #print(y_data) y_data=np.delete(y_data,0) x_data=frame[1:len(y_data)+1] #print(x_data) params, cov = curve_fit(log_func, x_data, y_data) a, b = params x_fit = np.linspace(min(x_data), max(x_data), 100) y_fit = log_func(x_fit, a, b) plt.plot(x_fit, y_fit, &#39;-&#39;,color=&#39;blue&#39; ,label=f"l中pt质量占比随时间变化曲线: $y={a:.8f}\\ln x + {b:.8f}$") y_data=medfilt(s_Pt_parition, kernel_size=5) #print(y_data) y_data=np.delete(y_data,0) x_data=frame[1:len(y_data)+1] #print(x_data) params, cov = curve_fit(log_func, x_data, y_data) a, b = params x_fit = np.linspace(min(x_data), max(x_data), 100) y_fit = log_func(x_fit, a, b) plt.plot(x_fit, y_fit, &#39;-&#39;,color=&#39;red&#39; ,label=f"s中pt质量占比随时间变化曲线: $y={a:.8f}\\ln x + {b:.8f}$") plt.legend() plt.savefig(&#39;pt num with frame.png&#39;,dpi=100) plt.show() plt.scatter(frame,D,c=&#39;green&#39;,marker=&#39;o&#39;) plt.legend(loc=&#39;center right&#39;, bbox_to_anchor=(0.98, 0.5)).set_visible(False) plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] plt.rcParams[&#39;axes.unicode_minus&#39;] = False y_data=medfilt(D, kernel_size=5) #print(y_data) y_data=np.delete(y_data,0) x_data=frame[1:len(y_data)+1] params, cov = curve_fit(log_func, x_data, y_data) a, b = params x_fit = np.linspace(min(x_data), max(x_data), 100) y_fit = log_func(x_fit, a, b) plt.plot(x_fit, y_fit, &#39;-&#39;,color=&#39;red&#39; ,label=f"D: $y={a:.8f}\\ln x + {b:.8f}$") plt.show() 如何优化?
10-21
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值