View与ViewGroup--实现QQ左滑删除

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.本博客仅用于学习,希望有所帮助,如有错误,请告知










  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值