Android的UI界面都是由View和ViewGroup及其派生类组合而成的。
其中,View是所有UI组件的基类,而ViewGroup是容纳这些组件的容器,其本身也是从View派生出来的。
AndroidUI界面的一般结构可参见下面的示意图:
其中:第二层的ViewGroup可以看作是一个同层的特殊view。这样就可以很方便的设计出好看的UI。
下面以一个简单的例子来说明如何设计出一个好的UI:
项目名称:点击左滑删除信息(这是自己的一个小Idea),转载请标明出处
不废话,效果如图所示:
这里就用到了上面提到的第二层的ViewGroup可以看作是一个同层的特殊view,其实就是把信息这一个LinearLayout(本身就是ViewGroup)看作一个view,然后把删除按钮(本质是一个TextView)作为一个View,然后实现删除按钮的隐藏(用前面一个"View"设置layout_width为match_parent,这样就可以实现隐藏了)与显示(其实就是重新设置前面的"View"的属性layout_width以及layout_marginLeft就可以实现效果了)就好了
项目源码如下:
message.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<!-- 最外的这一部分layout_width设置为match_parent以覆盖后面的按钮 -->
<LinearLayout
android:id="@+id/ll_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:orientation="horizontal" >
<!-- 头像 -->
<ImageView
android:id="@+id/iv_photo"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginLeft="20dp"
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp"
android:src="@drawable/ic_launcher" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:layout_marginLeft="10dp"
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp"
android:orientation="vertical">
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:orientation="horizontal">
<!-- 姓名 -->
<TextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:singleLine="true"
android:gravity="left"
android:text="@string/name"
android:textSize="24sp" />
<!-- 时间 -->
<TextView
android:id="@+id/tv_time"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:gravity="right"
android:singleLine="true"
android:text="@string/time"
android:textSize="20sp" />
</LinearLayout>
<!-- 短信内容 -->
<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginRight="20dp"
android:text="@string/content"
android:ellipsize="end"
android:textSize="20sp"
android:singleLine="true"/>
</LinearLayout>
</LinearLayout>
<!-- 隐藏的删除按钮 -->
<TextView
android:id="@+id/tv_delete"
android:layout_width="100dp"
android:layout_height="match_parent"
android:background="#FFFF0000"
android:gravity="center"
android:text="@string/btn_delete"
android:textSize="24sp" />
</LinearLayout>
activity_main.xml文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<!--cacheColorHint和listSelector两个属性是用来防止自己设置了背景,而ListView加载默认背景而出现的背景颜色不协调问题-->
<cn.gdin.message.MessageListView
android:id="@+id/message_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:cacheColorHint="@android:color/transparent"
android:listSelector="@android:color/transparent"
android:divider="@android:color/darker_gray"
android:dividerHeight="2dp" />
</RelativeLayout>
自定义控件MessageListView:
package cn.gdin.message;
import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.ListView;
public class MessageListView extends ListView {
private String tag = "测试MessageListView事件";
private int mScreenWidth; // 屏幕的宽度
private int mDownX; // 鼠标点击的X坐标
private int mDownY; // 鼠标点击的Y坐标
private boolean isShown; // 图片是否显示
private int mDeleteBtnWidth;// 删除按钮的宽度
private ViewGroup mPointChild; // 一个容器:用来存放鼠标点击的item里的所有view
private LinearLayout.LayoutParams mLayoutParams; // 用来重新设置布局的各属性
public MessageListView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MessageListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// 获取并设置屏幕宽度(只用到宽度而已)
// 1.获取当前上下文的WindowManager
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
// 2.利用WindowManager获取DisplayMetrics保存的信息
DisplayMetrics dm = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(dm); // 将信息保存在dm里
// 3.设置屏幕宽度
mScreenWidth = dm.widthPixels;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch(ev.getAction()){
case MotionEvent.ACTION_DOWN:
performActionDown(ev);
break;
case MotionEvent.ACTION_MOVE:
return performActionMove(ev);
case MotionEvent.ACTION_UP:
performActionUp();
break;
}
return super.onTouchEvent(ev);
}
private void performActionUp() {
Log.d(tag, "鼠标松开");
//鼠标松开时根据移动距离判断显不显示
//移动距离大于按钮宽度的一半时显示,否则不显示 move传不过来 不过可以再次在布局里获取
if(-mLayoutParams.leftMargin>=mDeleteBtnWidth/2){
mLayoutParams.leftMargin = -mDeleteBtnWidth;
isShown = true ;
}else{
turnToNormal();
}
mPointChild.getChildAt(0).setLayoutParams(mLayoutParams);
}
private boolean performActionMove(MotionEvent ev) {
Log.d(tag, "鼠标移动");
// 获取鼠标的位置
int nowX = (int) ev.getX();
int nowY = (int) ev.getY();
Log.d(tag, "鼠标移动:获取XY成功");
// 移动的方向是向左 135度到225度之间的方向都属于向左
if (Math.abs(nowX - mDownX) > Math.abs(nowY) - mDownY) {
Log.d(tag, "鼠标移动:mDownX="+mDownX); //为什么有问题?
if (nowX < mDownX) { //如果向左 应该可以去掉吧???????????
Log.d(tag, "鼠标移动:向左判断成功");
// 计算左滑距离
int move = nowX - mDownX; // move是一个负数
// 如果左滑距离大于按钮的宽度的话,为防止右边空白,将距离设置为按钮宽度
if (-move > mDeleteBtnWidth) {
move = -mDeleteBtnWidth; // 左滑设置为负数
}
// 重新设置布局属性的值
Log.d(tag, "鼠标移动:重新设置布局属性的值");
// mLayoutParams = (LinearLayout.LayoutParams)
// mPointChild.getChildAt(0).getLayoutParams();
// //不用重新获取,在鼠标点击的时候已经获取到了
mLayoutParams.leftMargin = move;
mPointChild.getChildAt(0).setLayoutParams(mLayoutParams);
}
return true; //调用系统的onTouchEvent方法
}
return isShown; //调用自定义的onTouchEvent方法消耗事件
}
private void performActionDown(MotionEvent ev) {
Log.d(tag, "鼠标按下");
// 鼠标按下
// 如果当前的删除按钮是显示的话,就恢复初始状态
if (isShown) {
turnToNormal();
}
// 获取鼠标的位置
mDownX = (int) ev.getX();
mDownY = (int) ev.getY();
// 获取鼠标点击的item在当前可视化视图里的位置(position)
mPointChild = (ViewGroup) getChildAt(pointToPosition(mDownX, mDownY)
- getFirstVisiblePosition());
// 获取删除按钮的宽度
mDeleteBtnWidth = mPointChild.getChildAt(1).getLayoutParams().width;
// 设置第一个view的b宽度为屏幕宽度
// 得到第一个view的“layout_”
mLayoutParams = (LinearLayout.LayoutParams) mPointChild.getChildAt(0)
.getLayoutParams();
// 设置得到第一个view的“layout_width”的值是屏幕的宽度
mLayoutParams.width = mScreenWidth;
mPointChild.getChildAt(0).setLayoutParams(mLayoutParams); // 设置的是第一个view
}
public void turnToNormal() {
// 初始状态时,不设置距左外边距
mLayoutParams.leftMargin = 0;
// 获取容器里的第一个view,重新设置它的布局属性的值
mPointChild.getChildAt(0).setLayoutParams(mLayoutParams);
// 初始状态时,删除按钮不可见
isShown = false;
}
//不显示删除的时候可以点击
public boolean ifCanClick(){
return !isShown;
}
}
Message类
package cn.gdin.message;
import java.util.Date;
public class Message {
private String name;
private String time;
private String content;
public Message() {
}
public Message(String name, String time, String content) {
super();
this.name = name;
this.time = time;
this.content = content;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTime() {
return time;
}
public void setTime(String string) {
this.time = string;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "Message [name=" + name + ", time=" + time + ", content="
+ content + "]";
}
}
MainActivity文件
package cn.gdin.message;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
private MessageListView messageListView;
private Message message;
private ArrayList<Message> mData = new ArrayList<Message>();
SimpleDateFormat df = new SimpleDateFormat("MM-dd HH");//设置日期格式
private String TAG = "MainActivity中测试错误";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for(int i=1;i<=30;i++){
message = new Message();
message.setName("小明"+i);
message.setTime( df.format(new Date())+"时" );
message.setContent("这是第"+i+"条信息");
mData.add(message);
}
//Log.i(TAG, "以下显示集合中的某一数据");
//Log.d(TAG, "集合有:"+mData.size()+"个元素");
messageListView = (MessageListView) findViewById(R.id.message_list);
messageListView.setAdapter(new MessageAdapter());
messageListView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
if(messageListView.ifCanClick()){
Toast.makeText(MainActivity.this, mData.get(position).toString(), Toast.LENGTH_SHORT).show();
}
}
});
}
class MessageAdapter extends BaseAdapter{
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int position) {
return mData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//重用convertView
if(null == convertView){
convertView = View.inflate(MainActivity.this, R.layout.message, null);
}
TextView tv_name = (TextView) convertView.findViewById(R.id.tv_name);
TextView tv_time = (TextView) convertView.findViewById(R.id.tv_time);
TextView tv_content = (TextView) convertView.findViewById(R.id.tv_content);
TextView delete = (TextView) convertView.findViewById(R.id.tv_delete);
//Log.i(TAG, "tv_name中的数据如下");
//Log.i(TAG, "tv_name中的数据是"+mData.get(position).getName().toString());
tv_name.setText(mData.get(position).getName().toString());
tv_time.setText(""+mData.get(position).getTime());
tv_content.setText(mData.get(position).getContent().toString());
final int pos = position;
//点击删除
delete.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
mData.remove(pos); //先去掉数据
notifyDataSetChanged(); //重新加载数据
messageListView.turnToNormal(); //重新将选择的可视item的删除按钮隐藏
}
});
return convertView;
}
}
}
string.xml文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">短信</string>
<string name="hello_world">Hello world!</string>
<string name="btn_delete">删除</string>
<string name="content">这是短信内容</string>
<string name="name">姓名</string>
<string name="time">时间</string>
</resources>
至此,项目基本完成了,代码中已经加了注释,再次就不再解释了。
后注:
1.这只是实现了一点点小功能而已,后面会在此基础上加上一下功能完善一下该项目
2.转载请注明出处
3.本博客仅用于学习,希望有所帮助,如有错误,请告知