我们以后摄Photo为例,跟踪下CameraAPP顶部的Icon是怎么加载上去的。
首先,UI相关的继承关系如下:
AutoPhotoUI --> PhotoUI —>DreamUI
具体布局是:TopPanelLayout
public class TopPanelLayout extends LinearLayout
看下自定义view的内容
public void addTopButtons(ArrayList<TopPanelUtil.IconAndDes> list) {
if(list.size() <= 0){
return;
}
mList.addAll(list);
int length = mList.size();
for(int i = 0; i < length; i++){
TopPanelUtil.IconAndDes value= mList.get(i);
addTopButton(value,i,length);
}
}
public void addTopButtons(TopPanelUtil.IconAndDes value, int index){
mList.add(index,value);
addTopButton(value,index,mList.size());
if (index == 0 && mList.size() >= 2){
((ViewGroup) this.getChildAt(1)).getChildAt(0).setVisibility(View.VISIBLE);
}
}
public void removeTopButton(Integer id){
int index = findItemIndex(id);
if(index == -1){
return;
}
TopPanelUtil.IconAndDes value = mList.remove(index);
if(index == 0) {
((ViewGroup) this.getChildAt(1)).getChildAt(0).setVisibility(View.GONE);
}
this.removeViewAt(index);
}
很熟悉,有一些add、remove相关的,应该是view的操作。并且数据都转换成了 TopPanelUtil.IconAndDes 类型在传递。
展讯平台的TopLayout做的比较好,就是无论顶部有几个Icon,都是等间距的,如果只有两个的话,就是左右两边各一个Icon,这样看起来比较舒服。
具体的控制是通过LayoutParams做的
private void addTopButton(TopPanelUtil.IconAndDes value, int index, int length){
boolean isLeftEdge = index == 0 && length != 1;//如果只有一个Icon的话,还是添加widget,这样就能在右侧显示了
LinearLayout frame = generateButtonLayout(value, isLeftEdge);
addTopButton(frame,index,isLeftEdge);
}
private void addTopButton(LinearLayout frame, int index, boolean leftEdge) {
LinearLayout.LayoutParams lp;
if(leftEdge){//最左侧的View不设置weiget
lp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
} else {//除了最左侧的View,其他view都设置相等的weiget
lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT,1);
}
this.addView(frame,index,lp);
}
Icon分为两种:普通Button 和 MultitoggleImageButton
private LinearLayout generateButtonLayout(TopPanelUtil.IconAndDes value, boolean leftEdge) {
LinearLayout frame = null;
if(value.buttonType == R.integer.button_type_multitoggleimagebutton){
frame = addMultitoggleImageButton(value,leftEdge);
} else if ( value.buttonType == R.integer.button_type_rotateimagebutton ){
frame = addRotateImageButton(value,leftEdge);
}
return frame;
}
private LinearLayout addMultitoggleImageButton(TopPanelUtil.IconAndDes value, boolean leftEdge){
int layoutId = R.layout.top_panel_item_multitoggle_image_button;
LayoutInflater lf = LayoutInflater.from(getContext());
LinearLayout frame = (LinearLayout)lf.inflate(layoutId,null);
MultiToggleImageButton button = (MultiToggleImageButton) frame.getChildAt(1);
button.setId(value.id);
button.overrideContentDescriptions(value.desID);
button.overrideImageIds(value.iconID);
button.setState(0);
if(leftEdge){
frame.getChildAt(0).setVisibility(View.GONE);
}
return frame;
}
vie拿到数据后做的相关的处理就是这些了,最开始看到的addTopButtons相关的方法,便是数据来源了。现在跟踪下数据来自什么逻辑。
DreamUI中的关键方法
@Override
public void fitTopPanel(ViewGroup topPanelParent){
//1 加载布局
LayoutInflater lf = LayoutInflater.from(topPanelParent.getContext());
mTopPanel = (TopPanelLayout)(lf.inflate(R.layout.top_panel_layout, null));
//2 获取xml的数据配置
int configID = getTopPanelConfigID();
ArrayList<TopPanelUtil.IconAndDes> list = TopPanelUtil.generateTopPanelList(topPanelParent.getContext(),configID);
//3 过滤xml配置的数据
filterTopPanelConfig(list);
// 4 addTop来了,将过滤后的数据传给view去显示
mTopPanel.addTopButtons(list);
((CameraActivity)topPanelParent.getContext()).getButtonManager().load(mTopPanel);
bindTopPanelButton();
//5 填充好了TopPanelLayout之后,在把它放到topPanelParent中
FrameLayout.LayoutParams lay = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
topPanelParent.addView(mTopPanel,lay);
updateTopPanelButtonStatus();
}
上面代码中的1,2,3,4步写得很清楚了,在不同的Module下,显示的Icon不同就是因为第二步中的xml配置数据了。
基类DreamUI中 getTopPanelConfigID 方法并未实现,需要具体的moduleUI去实现
在AutoPhotoUI中看到(不同Module有自己的配置)
@Override
protected int getTopPanelConfigID() {
return R.array.auto_photo_top_panel;
}
<array name="auto_photo_top_panel">
<item>@array/flash_photo_front_led_config</item>
<item>@array/hdr_config</item>
<item>@array/motionphoto_config</item>
<item>@array/makeup_display_photo_config</item>
<item>@array/camera_settings_config</item>
</array>
我们看到应该是有5个配置了。
前面我们说到Icon有两种类型,在xml中我们可以很清楚的看到两种类型的配置和差异
<array name="flash_photo_front_led_config">
<item>@integer/button_type_multitoggleimagebutton</item>
<item>@integer/flash_toggle_button_dream</item>
<item>@array/camera_flash_descriptions</item>
<item>@array/dream_camera_front_led_flashmode_icons</item>
</array>
<array name="camera_settings_config">
<item>@integer/button_type_rotateimagebutton</item>
<item>@integer/settings_button_dream</item>
<item>@drawable/ic_setting_indicator_unisoc</item>
</array>
显然,button_type_rotateimagebutton 是少一个description的配置哦。
在上面的TopPanelLayout中我们看到它将传入的数据转换成了 TopPanelUtil.IconAndDes类型
public static class IconAndDes {
public int buttonType;
public int id;
public int desID;
public int iconID;
public IconAndDes(TypedArray type){
buttonType = type.getResourceId(0,-1);
id = type.getResourceId(1,-1);
if(buttonType == R.integer.button_type_rotateimagebutton){
iconID = type.getResourceId(2,-1);
} else if( buttonType == R.integer.button_type_multitoggleimagebutton ){
desID = type.getResourceId(2,-1);
iconID = type.getResourceId(3,-1);
}
}
}
这个类的成员变量一个看就和xml的配置比较相似了吧,没错,xml配置的数据就是转成了这个对象来装的,并且这里配合xml两种类型的button,也做了type的区分。
数据加载进来了,第3步,filterTopPanelConfig还是由各个Module去决定。
所以,我们在xml配置了设置项,但是不一定会显示出来,因为还可能在java的filter方法中过滤掉哦。
topPanelLayout填充之后,再将其add到topPanelParent,这样设置项就显示出来了。