最近少了写博客,可能最近忙吧,工作上忙,因为工作原因也忙于学习,也没记录什么了,也没有按照之前的计划去学习了。现在就记录一下最近学到的。
要做Android应用,界面设计少不了,可惜之前一直在用WindowPhone的,对别的系统的界面风格严重不了解,稍留意了一下,发现有几种风格的,上网找了代码学习了一下,还是能做出来的
豌豆荚的布局
这种布局类似用Tab来布局,通过左右滑动切换不同的界面,或者点击顶部的文字来切换。不过在这里我并没有用上TabHost,对整个页面来说我把它切成了3部分:文字导航、绿色的那小条子,还有下面主要的内容
- 最上面的文字导航我就用一个线性布局把TextView排列起来
- 绿色的小条子也是用了一个线性布局里面放了一个ImageView,这个ImageView是需要加动画的
- 主要内容我就用了ViewPager,这是一个好东西,滑动切换时有效果,这里是用到了碎片Fragment,虽然说是Android3.0后才加上去的东西,但是2.3的系统还是能用得上。
然后用一个线性布局把上面这三块排列起来就行了
下面就展示一下布局的代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" > <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" > <LinearLayout
android:id="@+id/imf_linearLayout1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="5dip"
android:paddingTop="10dip" > <TextView
android:id="@+id/imfLbRealTime"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1.0"
android:gravity="center"
android:text="Tab1"
android:textColor="@color/select"/> <TextView
android:id="@+id/imfLbOnLine"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1.0"
android:gravity="center"
android:text="Tab2"
android:textColor="@color/unselect"/>
</LinearLayout> <LinearLayout
android:layout_width="match_parent"
android:layout_height="2dip"
android:layout_gravity="bottom"
android:orientation="vertical"
android:paddingBottom="3dip"
android:background="#ddd"> <ImageView
android:id="@+id/imfImgButtomLine"
android:layout_width="40dip"
android:layout_height="2dip"
android:scaleType="matrix"
android:src="@color/select" />
</LinearLayout>
</LinearLayout> <android.support.v4.view.ViewPager
android:id="@+id/imfVpContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1.0"
android:flipInterval="30"
android:persistentDrawingCache="animation" /> </LinearLayout>
这里为了兼容,ViewPager也是用了support.v4里面的视图,导航的条子的宽度这里是乱设的,因为它到时的宽度会在java代码中进行设置。还有颜色呢只是在color.xml里面定义的颜色而已
Java文件就不打算完全贴出来,因为这个可以用在碎片里面,也可以用在活动里面,我就用在了碎片里面。
在构造碎片的方法里面或者构造视图的方法里面,依次调用下面定义的方法
private void IniWidth()
{
DisplayMetrics dm=new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm);
int screenW = dm.widthPixels;
buttomLine=(ImageView)view.findViewById(R.id.domfImgButtomLine);
buttomLine.getLayoutParams().width=screenW/5;
buttomLine.setLayoutParams(buttomLine.getLayoutParams()); buttomLineWidth=buttomLine.getLayoutParams().width;
offset = (int) ((screenW / 5.0 - buttomLineWidth) / 2); positionOne = (int) (screenW / 5.0);
positionTwo = positionOne * 2;
positionThree=positionOne * 3;
positionFour=positionOne * 4;
}
这个方法先获取获取当前屏幕的宽度,再通过宽度对小条的吃长度进行设置,最后用到的几个position是后面在给小条动画效果时用到的值,它们都是int类型,记录着每个导航标题的位置。
private void IniViews()
{
lbCheck=(TextView)view.findViewById(R.id.domfLbCheck);
lbPublish=(TextView)view.findViewById(R.id.domfLbPublish);
lbQuery=(TextView)view.findViewById(R.id.domfLbQuery);
lbRecommand=(TextView)view.findViewById(R.id.domfLbRecommand);
lbSend=(TextView)view.findViewById(R.id.domfLbSend); lbQuery.setOnClickListener(new MyOnClickListener(0));
lbCheck.setOnClickListener(new MyOnClickListener(1));
lbPublish.setOnClickListener(new MyOnClickListener(2));
lbSend.setOnClickListener(new MyOnClickListener(3));
lbRecommand.setOnClickListener(new MyOnClickListener(4));
}
视图设置,给相应的导航标题对象添加引用,以及绑定事件,绑定事件目的在于点击文字时会切换到相应的页面中去,事件的定义如下
class MyOnClickListener implements View.OnClickListener {
private int index = 0; public MyOnClickListener(int i) {
index = i;
} @Override
public void onClick(View v) {
mPager.setCurrentItem(index);
}
};
最后设置ViewPager了
private void IniViewPager()
{
mPager=(ViewPager)view.findViewById(R.id.domfVpContent); ArrayList<Fragment> fragLst=new ArrayList<Fragment>();
checkFrag =new DataOptionCheckFragment();
publishFrag =new DataOptionPublishFragment();
queryFrag =new DataOptionQueryFragment();
recommandFrag =new DataOptionRecommandFragment();
sendFrag =new DataOptionSendFragment();
fragLst.add(queryFrag);
fragLst.add(checkFrag);
fragLst.add(publishFrag);
fragLst.add(sendFrag);
fragLst.add(recommandFrag); adapter=new MyViewPageAdapter(getActivity().getSupportFragmentManager(),fragLst);
mPager.setAdapter(adapter);
mPager.setCurrentItem(0);
mPager.setOnPageChangeListener(new MyOnPageChangeListener());
mPager.setCurrentItem(0);
}
这里主要是设置构造相应的碎片,然后添加到相应的adapter里面,加入到ViewPager里面,重点在于PageChange事件的定义
class MyOnPageChangeListener implements OnPageChangeListener { @Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub } @Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub } @Override
public void onPageSelected(int selectedIndex) { Animation animation=null;
if(selectedIndex==currIndex)
{ }
switch (selectedIndex) {
case 0:
if (currIndex == 1) {
animation = new TranslateAnimation(positionOne, 0, 0, 0);
lbCheck.setTextColor(resources.getColor(R.color.unselect));
} else if (currIndex == 2) {
animation = new TranslateAnimation(positionTwo, 0, 0, 0);
lbPublish.setTextColor(resources.getColor(R.color.unselect));
} else if (currIndex == 3) {
animation = new TranslateAnimation(positionThree, 0, 0, 0);
lbSend.setTextColor(resources.getColor(R.color.unselect));
}else if (currIndex == 4) {
animation = new TranslateAnimation(positionFour, 0, 0, 0);
lbRecommand.setTextColor(resources.getColor(R.color.unselect));
} else if(selectedIndex==currIndex){
animation=new TranslateAnimation(0, 0, 0, 0);
lbQuery.setTextColor(resources.getColor(R.color.unselect));
}
lbQuery.setTextColor(resources.getColor(R.color.select));
break;
case 1:
if (currIndex == 0) {
animation = new TranslateAnimation(offset, positionOne, 0, 0);
lbQuery.setTextColor(resources.getColor(R.color.unselect));
} else if (currIndex == 2) {
animation = new TranslateAnimation(positionTwo, positionOne, 0, 0);
lbPublish.setTextColor(resources.getColor(R.color.unselect));
} else if (currIndex == 3) {
animation = new TranslateAnimation(positionThree, positionOne, 0, 0);
lbSend.setTextColor(resources.getColor(R.color.unselect));
}else if (currIndex == 4) {
animation = new TranslateAnimation(positionFour, positionOne, 0, 0);
lbRecommand.setTextColor(resources.getColor(R.color.unselect));
}else if(selectedIndex==currIndex){
animation=new TranslateAnimation(0, positionOne, 0, 0);
lbQuery.setTextColor(resources.getColor(R.color.unselect));
}
lbCheck.setTextColor(resources.getColor(R.color.select));
break;
case 2:
if (currIndex == 0) {
animation = new TranslateAnimation(offset, positionTwo, 0, 0);
lbQuery.setTextColor(resources.getColor(R.color.unselect));
} else if (currIndex == 1) {
animation = new TranslateAnimation(positionOne, positionTwo, 0, 0);
lbCheck.setTextColor(resources.getColor(R.color.unselect));
} else if (currIndex == 3) {
animation = new TranslateAnimation(positionThree, positionTwo, 0, 0);
lbSend.setTextColor(resources.getColor(R.color.unselect));
}else if (currIndex == 4) {
animation = new TranslateAnimation(positionFour, positionTwo, 0, 0);
lbRecommand.setTextColor(resources.getColor(R.color.unselect));
}else if(selectedIndex==currIndex){
animation=new TranslateAnimation(0, positionTwo, 0, 0);
lbQuery.setTextColor(resources.getColor(R.color.unselect));
}
lbPublish.setTextColor(resources.getColor(R.color.select));
break;
case 3:
if (currIndex == 0) {
animation = new TranslateAnimation(offset, positionThree, 0, 0);
lbQuery.setTextColor(resources.getColor(R.color.unselect));
} else if (currIndex == 1) {
animation = new TranslateAnimation(positionOne, positionThree, 0, 0);
lbCheck.setTextColor(resources.getColor(R.color.unselect));
} else if (currIndex == 2) {
animation = new TranslateAnimation(positionTwo, positionThree, 0, 0);
lbPublish.setTextColor(resources.getColor(R.color.unselect));
}else if (currIndex == 4) {
animation = new TranslateAnimation(positionFour, positionThree, 0, 0);
lbRecommand.setTextColor(resources.getColor(R.color.unselect));
}else if(selectedIndex==currIndex){
animation=new TranslateAnimation(0, positionThree, 0, 0);
lbQuery.setTextColor(resources.getColor(R.color.unselect));
}
lbSend.setTextColor(resources.getColor(R.color.select));
break;
case 4:
if (currIndex == 0) {
animation = new TranslateAnimation(offset, positionFour, 0, 0);
lbQuery.setTextColor(resources.getColor(R.color.unselect));
} else if (currIndex == 1) {
animation = new TranslateAnimation(positionOne, positionFour, 0, 0);
lbCheck.setTextColor(resources.getColor(R.color.unselect));
} else if (currIndex == 2) {
animation = new TranslateAnimation(positionTwo, positionFour, 0, 0);
lbPublish.setTextColor(resources.getColor(R.color.unselect));
}else if (currIndex == 3) {
animation = new TranslateAnimation(positionThree, positionFour, 0, 0);
lbSend.setTextColor(resources.getColor(R.color.unselect));
}else if(selectedIndex==currIndex){
animation=new TranslateAnimation(0, positionFour, 0, 0);
lbQuery.setTextColor(resources.getColor(R.color.unselect));
}
lbRecommand.setTextColor(resources.getColor(R.color.select));
break; } currIndex=selectedIndex;
animation.setFillAfter(true);
animation.setDuration(300);
buttomLine.startAnimation(animation); } }
这里根据之前选中的标题是哪个,确定要把小滑块从原本位置移动到新位置上,把原本选中颜色的文字变回不选中,把新选中文字的颜色更替。
设置ViewPager时还涉及到一个adapter的定义,代码如下
public class MyViewPageAdapter extends FragmentPagerAdapter {
private ArrayList<Fragment> arrayList; public MyViewPageAdapter(FragmentManager fm) {
super(fm);
// TODO Auto-generated constructor stub
} public MyViewPageAdapter(FragmentManager fm,ArrayList<Fragment> arrayList)
{
super(fm);
this.arrayList=arrayList;
} @Override
public Fragment getItem(int position) {
// TODO Auto-generated method stub
return arrayList.get(position);
} @Override
public int getCount() {
// TODO Auto-generated method stub
return arrayList.size();
} @Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
有个争议的地方,就是对public Object instantiateItem (ViewGroup container, int position)方法的重写,在网上看对这个方法重写后,多次切换ViewPager之后内容就会被清空掉,但是我重写了之后内容才不会被清空,有次还尝试了无论有没有重写照样被清空。
底部Tab导航
这个用的是TabHost了,个人认为主要的是TabHost和TabWidget,而主要还是靠TabWidget这部分把Tab的效果呈现出来,而页面的内容最终还是靠碎片来实现,这里可以用FrameLayout显示一个碎片,也可以通过ViewPager显示,布局文件如下
<?xml version="1.0" encoding="utf-8"?>
<TabHost
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent"> <LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/tabEnableLayout"> <FrameLayout
android:id="@+id/realtabcontent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/> <TabWidget
android:id="@android:id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tabStripEnabled="false"
android:background="#04508b"
android:layout_weight="0"/> </ LinearLayout >
</TabHost>
然后java文件构造的方法中按顺序调用下面定义的方法
//设置tab选项的内容
private void SetViews()
{
fm=this.getActivity().getSupportFragmentManager(); tabHost=(TabHost)contentView. findViewById(android.R.id.tabhost);
tabWidget=(TabWidget)contentView. findViewById(android.R.id.tabs);
LinearLayout layout=(LinearLayout)tabHost.getChildAt(0);
TabWidget tw=(TabWidget)layout.getChildAt(1); relative1=(RelativeLayout)LayoutInflater.from(this.getActivity()).inflate(R.layout.tab_indicator_item, tw, false);
((ImageView)relative1.getChildAt(0)).setBackgroundResource(R.drawable.selector_tab_home);
((TextView)relative1.getChildAt(1)).setText("首页"); relative2=(RelativeLayout)LayoutInflater.from(this.getActivity()).inflate(R.layout.tab_indicator_item, tw, false);
((ImageView)relative2.getChildAt(0)).setBackgroundResource(R.drawable.selector_tab_data);
((TextView)relative2.getChildAt(1)).setText("数据"); relative3=(RelativeLayout)LayoutInflater.from(this.getActivity()).inflate(R.layout.tab_indicator_item, tw, false);
((ImageView)relative3.getChildAt(0)).setBackgroundResource(R.drawable.selector_tab_room);
((TextView)relative3.getChildAt(1)).setText("站房"); relative4=(RelativeLayout)LayoutInflater.from(this.getActivity()).inflate(R.layout.tab_indicator_item, tw, false);
((ImageView)relative4.getChildAt(0)).setBackgroundResource(R.drawable.selector_tab_more);
((TextView)relative4.getChildAt(1)).setText("更多"); indexFragment=new IndexFragment();
dataOptionFragment=new DataOptionFragment();
roomEnvFragment =new RoomEnvFragment();
moreFragment=new MoreFragment(); }
这里主要是获取了TabWidget,然后把逐个标签设置进去,标签的内容也是通过布局文件来设置。
private void SetTabHost()
{
tabHost.setup();
TabHost.OnTabChangeListener tabChangeListener = new TabHost.OnTabChangeListener(){
@Override
public void onTabChanged(String arg0) { catch(Exception ex)
{} if(arg0.equalsIgnoreCase("Home")){
ChangeFragment(indexFragment);
SELECT_TAB=1;
} else if(arg0.equalsIgnoreCase("Data")){
ChangeFragment(dataOptionFragment);
SELECT_TAB=2;
} else if(arg0.equalsIgnoreCase("Room")){
ChangeFragment(roomEnvFragment);
SELECT_TAB=3;
}
else if(arg0.equalsIgnoreCase("More")){
ChangeFragment(moreFragment);
SELECT_TAB=4;
} else
{
switch (SELECT_TAB) {
default:
case 1:
ChangeFragment(indexFragment);
break;
case 2:
ChangeFragment(dataOptionFragment);
break;
case 3:
ChangeFragment(roomEnvFragment);
break;
case 4:
ChangeFragment(moreFragment);
break;
}
}
}
};
tabHost.setCurrentTab(0);
tabHost.setOnTabChangedListener(tabChangeListener);
IniTab();
tabHost.setCurrentTab(0);
}
这里大部分代码都是定义Tab切换的事件,因为在Android中的TabHost跟之前在WinFor中的TabControl那样,标签切换了,内容跟着切换。切换时调用了一个方法,处理碎片的切换
private void ChangeFragment(Fragment frag)
{
ft= fm.beginTransaction();
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.replace(R.id.realtabcontent, frag);
ft.commit();
}
还有另一个方法是真的把Tab添加都TabHost里面去了
//增添Tab的内容
private void IniTab()
{
TabSpec tSpecHome=tabHost.newTabSpec("Home");
TabSpec tSpecData=tabHost.newTabSpec("Data");
TabSpec tSpecRoom=tabHost.newTabSpec("Room");
TabSpec tSpecMore=tabHost.newTabSpec("More"); tSpecHome.setIndicator(relative1);
tSpecHome.setContent(new MyTabContent(this.getActivity().getBaseContext()));
tSpecData.setIndicator(relative2);
tSpecData.setContent(new MyTabContent(this.getActivity().getBaseContext()));
tSpecRoom.setIndicator(relative3);
tSpecRoom.setContent(new MyTabContent(this.getActivity().getBaseContext()));
tSpecMore.setIndicator(relative4);
tSpecMore.setContent(new MyTabContent(this.getActivity().getBaseContext())); tabHost.addTab(tSpecHome);
tabHost.addTab(tSpecData);
tabHost.addTab(tSpecRoom);
tabHost.addTab(tSpecMore); }
侧栏菜单
这个感觉比较简单,但是效果没那么好,主要用到的是FrameLayout布局(或者RelativeLayout布局)和ListView,因为FrameLayout只能让内容显示在左上角(RelativeLayout默认也是把内容显示在左上角),所以把ListView设置在FrameLayout(或RelativeLayout)最下面就可以了。
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<RelativeLayout
android:id="@+id/divTitle"
android:layout_width="fill_parent"
android:layout_height="45dp"
android:orientation="horizontal"
android:background="#007ACC"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="22dp"
android:layout_centerInParent="true"
android:textColor="@android:color/white"
android:text="@string/app_name"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:text="刷新"
android:id="@+id/titleBtnRefresh"
/> </RelativeLayout> <fragment
android:id="@+id/titles"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
class="com.sysu.cityenv.androidapp.Fragments.TabHostFragment"/>
</LinearLayout> <ListView
android:id="@+id/left_menu"
android:layout_height="fill_parent"
android:layout_width="120dp"
android:background="#F0F0F0"/> </RelativeLayout>
ListView设置的代码就不列举了,拉出侧栏的代码是通过触控事件来实现的,在点击下去的时候记录开始位置,到提起来的时候就判断移动的方向,从而把ListView设置成显示和隐藏
@Override
public boolean onTouchEvent(MotionEvent event)
{
float xDelta=(float) Math.abs(mPostion-event.getX());
if(event.getAction()==MotionEvent.ACTION_DOWN){
mPostion= event.getX();
mPositionY=event.getY(); }
if(event.getAction()==MotionEvent.ACTION_UP&&xDelta>70)
{
if(xDelta>Math.abs(mPositionY-event.getY())){ boolean isVisiable=leftMenu.getVisibility()==View.VISIBLE;
if(mPostion>event.getX()&&isVisiable) Hide();
else if(mPostion<event.getX()&&mPostion<=sWidth*0.1) Show();
}
} return super.onTouchEvent(event);
}
不过侧栏滑出来了,后面的内容还是会有事件响应的,就算在这里把返回值设成false也没用(true表明在事件冒泡中还需要处理,false则表示不需要处理了),因为在整个事件冒泡过程中,这个OnTouch的时间在最后才触发,那只能把后面视图作处理,让它的返回是false,我的处理是这样子的,把ListView下面的内容放在一个Layout的布局中去,那个Layout是自己拓展某个Layout视图,重写它的onInterceptTouchEvent方法,让它在适当的时候返回false,不对触控有响应,那么点击事件也没响应了,因为Click事件是包在了两个Touch事件里面的(DOWN和UP)。
@Override
public boolean onTouchEvent(MotionEvent event)
{
float xDelta=(float) Math.abs(mPostion-event.getX());
if(event.getAction()==MotionEvent.ACTION_DOWN){
mPostion= event.getX();
mPositionY=event.getY(); if(mPostion<=sWidth*0.1)
((MyLinearLayout)findViewById(R.id.tabEnableLayout)).SetControlEnable(false);
}
if(event.getAction()==MotionEvent.ACTION_UP&&xDelta>70)
{
if(xDelta>Math.abs(mPositionY-event.getY())){ boolean isVisiable=leftMenu.getVisibility()==View.VISIBLE;
if(mPostion>event.getX()&&isVisiable) Hide();
else if(mPostion<event.getX()&&mPostion<=sWidth*0.1) Show();
}
}
else if(event.getAction()==MotionEvent.ACTION_UP&&mPostion<=sWidth*0.1)
{
((MyLinearLayout)findViewById(R.id.tabEnableLayout)).SetControlEnable(true);
} return super.onTouchEvent(event);
}
代码就成这样子,但是这样有个弊端是,滑动的距离太小的时候,侧栏没显示出来,但是侧栏下面的内容已经停止了事件响应,暂时没好办法去处理。