本文转载自:http://www.tuicool.com/articles/iy6Zvyn
这是手机QQ2013官方版的登录界面:
这个是我自己做出来的 QQ登录界面:
当然与官方版相比还是有很大的差距,不过对于学习安卓控件的使用已经足够了。
为实现上述界面,需要有几个关键的知识点需要学习:
一、实现圆角的效果——学会使用描述背景的drawable/中的 xml文件
需要在drawable文件夹中创建xml文件,文件的父控件类型为shape,在shape父控件中,有<solid/> <corners/> <stroke/> <padding/> 等属性,分别处理背景的填充颜色、边角的曲率、边框线的宽度和颜色、上下左右内边框(即背景超出使用改背景的空间的宽度)
例如,若想实现一个圆角的ImageButton,可以创建一个 fillet_shape.xml文件
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <solid android:color="#ffffff"/> <corners android:radius="10px"/> <padding android:left="3dip" android:top="3dip" android:right="3dip" android:bottom="3dip"/> </shape>
然后在Activity类中用ImageButton的实例设置setBackgroundResource(); 或者在xml布局文件中在配置控件属性使用 android:background="@drawable/fillet_shape"
注意这里在配置好背景之后,在为ImageView设置显示的图片时,只能使用setImageResource()而不能使用setBackgroundResource();
学会这点很重要,后面就可以举一反三,本例中使用该方法为 EditText设置了边框,为ListView的每一个Item设置了边框,为按钮设置了圆角背景。就不再特殊说明
二、RelativeLayout中控件的布局问题
不同控件之间是可以覆盖的,注意在布局文件中后配置的空间可以覆盖掉之前配置的空间,所以本例在布局文件中让ListView控件放在了最后,另外如果想要一个控件暂时消失
可以使用setVisibility(View.GONE);的方法,这样改控件消失以后被覆盖的空间就可以正常使用了。另外本例在EditText中添加按钮并没有自定义EditText,而是直接通过布局文件的描述将联系人游标(小箭头)嵌在了Edittext中。注意这里一般不使用View.INVISIBLE,这样控件并未消失
三、notifyDataSetChanged方法是BaseAdapter的方法,所以可以在构造的适配器内部或者创建的适配器对象使用。
四、并不是只有Button可以设置OnClickListener 实际上很多常见的空间都可以使用,如EditText或者TextView ,这个应该是属于View的抽象方法
五、ListView如何调整每一个Item边框的宽度并且避免Item之间的分割线颜色太深?
方法就是上面介绍的自定义drawable/ 中xml文件,来配置边和背景属性,另外在配置ListView控件的属性时 设置android:divider="#aaaaaa" android:dividerHeight="0px" 这样可以是ListItem的边框做出上图所示的效果。
六、怎样解决ListView中添加Button之后就不响应单击事件的问题?
原因是Button抢夺了焦点,最简单的解决办法是:在自定义的每一个ListItem的布局文件中在根标签的属性中添加上 android:descendantFocusability="blocksDescendants" 即拒绝ListItem中的子控件获得焦点
七、怎样实现在点击某个控件以外的屏幕区域就使该控件消失的效果?本例中实现在点击ListView以外的区域就会使ListView消失的效果。
方法是覆写MainActivity的onTouchEvent()方法,根据点击的坐标(x,y)与目标控件通过getLocation获得的控件左上角坐标,再结合目标控件的宽和高,判断点击的点是否在控件内,进而决定对该控件执行怎样的操作。
例子:
@Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub if(event.getAction()==MotionEvent.ACTION_DOWN && isVisible){ int[] location=new int[2]; //调用getLocationInWindow方法获得某一控件在窗口中左上角的横纵坐标 loginList.getLocationInWindow(location); //获得在屏幕上点击的点的坐标 int x=(int)event.getX(); int y=(int)event.getY(); if(x<location[0]|| x>location[0]+loginList.getWidth() || y<location[1]||y>location[1]+loginList.getHeight()){ isIndicatorUp=false; isVisible=false; listIndicatorButton.setBackgroundResource(R.drawable.indicator_down); loginList.setVisibility(View.GONE); //让ListView列表消失,并且让游标向下指! } } return super.onTouchEvent(event); }
以上就是我在写程序的过程中遇到的一些难题,天有些晚了,直接上所有的代码吧。。
首先布局文件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" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" android:background="#dde1ee" > <ImageView android:id="@+id/myImage" android:layout_width="70dip" android:layout_height="70dip" android:layout_marginTop="65dip" android:layout_centerHorizontal="true" android:background="@drawable/fillet_shape"/> <EditText android:id="@+id/qqNum" android:layout_width="match_parent" android:layout_height="40dip" android:layout_marginLeft="30dip" android:layout_marginRight="30dip" android:layout_marginTop="15dip" android:paddingLeft="50dip" android:layout_below="@id/myImage" android:inputType="number" android:background="@drawable/qqnum_edit"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="账号" android:textSize="8pt" android:textColor="@android:color/darker_gray" android:layout_alignLeft="@id/qqNum" android:layout_alignTop="@id/qqNum" android:layout_marginTop="9dip" android:layout_marginLeft="3dip"/> <EditText android:id="@+id/qqPassword" android:layout_width="match_parent" android:layout_height="40dip" android:paddingLeft="50dip" android:layout_marginLeft="30dip" android:layout_marginRight="30dip" android:layout_below="@id/qqNum" android:background="@drawable/qqnum_edit"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="密码" android:textSize="8pt" android:textColor="@android:color/darker_gray" android:layout_alignLeft="@id/qqPassword" android:layout_alignTop="@id/qqPassword" android:layout_marginTop="9dip" android:layout_marginLeft="3dip"/> <ImageButton android:id="@+id/qqListIndicator" android:layout_width="22dip" android:layout_height="20dip" android:layout_marginBottom="8dip" android:layout_marginRight="3dip" android:layout_alignBottom="@+id/qqNum" android:layout_alignRight="@+id/qqNum" android:background="@drawable/indicator_down" /> <ImageButton android:id="@+id/delete_button_edit" android:layout_width="18dip" android:layout_height="18dip" android:layout_marginBottom="8dip" android:layout_marginRight="3dip" android:layout_alignBottom="@+id/qqNum" android:layout_toLeftOf="@id/qqListIndicator" android:background="@drawable/delete_button_edit" android:visibility="gone"/> <Button android:id="@+id/qqLoginButton" android:layout_width="match_parent" android:layout_height="35dip" android:layout_below="@id/qqPassword" android:layout_alignLeft="@id/qqNum" android:layout_alignRight="@id/qqNum" android:layout_marginTop="20dip" android:background="@drawable/login_button_back" android:text="登录" android:textColor="@android:color/white"/> <TextView android:id="@+id/fetchPassword" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:layout_below="@id/qqLoginButton" android:layout_marginLeft="45dip" android:text="找回密码" android:textSize="7pt" android:textColor="#333355" android:gravity="bottom"/> <TextView android:id="@+id/registQQ" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/qqLoginButton" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_marginRight="45dip" android:layout_marginTop="5dip" android:text="注册账号" android:textSize="7pt" android:textColor="#333355" android:gravity="bottom"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/qqLoginButton" android:text="|" android:textSize="7pt" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:gravity="bottom"/> <ListView android:id="@+id/loginQQList" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@id/registQQ" android:layout_alignLeft="@id/qqNum" android:layout_alignRight="@id/qqNum" android:layout_below="@id/qqNum" android:focusable="true" android:focusableInTouchMode="true" android:visibility="gone" android:divider="#aaaaaa" android:dividerHeight="0px"/> </RelativeLayout>
<?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="35dip" android:orientation="horizontal" android:background="@drawable/list_item_border" android:descendantFocusability="blocksDescendants" > <ImageView android:id="@+id/login_userPhoto" android:layout_width="match_parent" android:layout_weight="4.7" android:layout_height="30dip" android:background="@drawable/contact_1" android:layout_marginLeft="10dip" android:layout_marginTop="5dip" android:layout_marginRight="15dip" android:layout_marginBottom="5dip"/> <TextView android:id="@+id/login_userQQ" android:layout_width="match_parent" android:layout_height="30dip" android:layout_marginTop="5dip" android:layout_weight="2" android:gravity="center_vertical" android:text="1234567890" android:textSize="7pt" /> <ImageButton android:id="@+id/login_deleteButton" android:layout_width="match_parent" android:layout_weight="5.8" android:layout_height="14dip" android:layout_marginTop="11dip" android:layout_marginRight="3dip" android:focusable="true" android:focusableInTouchMode="true" android:background="@drawable/deletebutton" /> </LinearLayout>
至于实现圆角等边框效果的xml布局文件就不再添加,上面第一条已经给出例子,可以根据需要的效果进行推广。
然后就是MainActivity文件,这里为了适配器类等够对Activity界面进行更改,将适配器类写成了MainActivity类的内部类,代码中有说明。另外相当一部分代码是为了实现一些细节性的东西,如EditText中游标的的方向变化,图片图案的变化,使用了一些常量。
package com.example.android_qq_login;
import java.util.ArrayList;
import java.util.HashMap;
import com.qqlist.contactor.UserInfo;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.*;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
public class MainActivity extends Activity {
TextView textFetchPassWord=null,textRegister=null;
Button loginButton=null;
ImageButton listIndicatorButton=null, deleteButtonOfEdit=null;
ImageView currentUserImage=null;
ListView loginList=null;
EditText qqEdit=null, passwordEdit=null;
private static boolean isVisible=false; //ListView是否可见
private static boolean isIndicatorUp=false; //指示器的方向
public static int currentSelectedPosition=-1;
//用于记录当前选择的ListView中的QQ联系人条目的ID,如果是-1表示没有选择任何QQ账户,注意在向
//List中添加条目或者删除条目时都要实时更新该currentSelectedPosition
String[] from={"userPhoto","userQQ","deletButton"};
int[] to={R.id.login_userPhoto,R.id.login_userQQ,R.id.login_deleteButton};
ArrayList<HashMap<String,Object>> list=null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textFetchPassWord=(TextView)findViewById(R.id.fetchPassword);
textRegister=(TextView)findViewById(R.id.registQQ);
loginButton=(Button)findViewById(R.id.qqLoginButton);
listIndicatorButton=(ImageButton)findViewById(R.id.qqListIndicator);
loginList=(ListView)findViewById(R.id.loginQQList);
list=new ArrayList<HashMap<String,Object>>();
currentUserImage=(ImageView)findViewById(R.id.myImage);
qqEdit=(EditText)findViewById(R.id.qqNum);
passwordEdit=(EditText)findViewById(R.id.qqPassword);
deleteButtonOfEdit=(ImageButton)findViewById(R.id.delete_button_edit);
qqEdit.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(qqEdit.getText().toString().equals("")==false){
deleteButtonOfEdit.setVisibility(View.VISIBLE);
}
}
});
deleteButtonOfEdit.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
currentUserImage.setImageResource(R.drawable.qqmain);
qqEdit.setText("");
currentSelectedPosition=-1;
deleteButtonOfEdit.setVisibility(View.GONE);
}
});
UserInfo user1=new UserInfo(R.drawable.contact_0,"1234567",R.drawable.deletebutton);
UserInfo user2=new UserInfo(R.drawable.contact_1,"10023455",R.drawable.deletebutton);
addUser(user1);
addUser(user2);
//设置当前显示的被选中的账户的头像
if(currentSelectedPosition==-1){
currentUserImage.setImageResource(R.drawable.qqmain);
qqEdit.setText("");
}
else{
currentUserImage.setImageResource((Integer)list.get(currentSelectedPosition).get(from[0]));
qqEdit.setText((String)list.get(currentSelectedPosition).get(from[1]));
}
MyLoginListAdapter adapter=new MyLoginListAdapter(this, list, R.layout.layout_list_item, from, to);
loginList.setAdapter(adapter);
loginList.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
// TODO Auto-generated method stub
currentUserImage.setImageResource((Integer)list.get(arg2).get(from[0]));
qqEdit.setText((String)list.get(arg2).get(from[1]));
currentSelectedPosition=arg2;
//相应完点击后List就消失,指示箭头反向!
loginList.setVisibility(View.GONE);
listIndicatorButton.setBackgroundResource(R.drawable.indicator_down);
System.out.println("---------Selected!!");
}
});
listIndicatorButton.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(isIndicatorUp){
isIndicatorUp=false;
isVisible=false;
listIndicatorButton.setBackgroundResource(R.drawable.indicator_down);
loginList.setVisibility(View.GONE); //让ListView列表消失
}
else{
isIndicatorUp=true;
isVisible=true;
listIndicatorButton.setBackgroundResource(R.drawable.indicator_up);
loginList.setVisibility(View.VISIBLE);
}
}
});
}
//继承onTouchEvent方法,用于实现点击控件之外的部分使控件消失的功能
private void addUser(UserInfo user){
HashMap<String,Object> map=new HashMap<String,Object>();
map.put(from[0], user.userPhoto);
map.put(from[1], user.userQQ);
map.put(from[2], user.deleteButtonRes);
list.add(map);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
if(event.getAction()==MotionEvent.ACTION_DOWN && isVisible){
int[] location=new int[2];
//调用getLocationInWindow方法获得某一控件在窗口中左上角的横纵坐标
loginList.getLocationInWindow(location);
//获得在屏幕上点击的点的坐标
int x=(int)event.getX();
int y=(int)event.getY();
if(x<location[0]|| x>location[0]+loginList.getWidth() ||
y<location[1]||y>location[1]+loginList.getHeight()){
isIndicatorUp=false;
isVisible=false;
listIndicatorButton.setBackgroundResource(R.drawable.indicator_down);
loginList.setVisibility(View.GONE); //让ListView列表消失,并且让游标向下指!
}
}
return super.onTouchEvent(event);
}
/**
* 为了便于在适配器中修改登录界面的Activity,这里把适配器作为
* MainActivity的内部类,避免了使用Handler,简化代码
* @author DragonGN
*
*/
public class MyLoginListAdapter extends BaseAdapter{
protected Context context;
protected ArrayList<HashMap<String,Object>> list;
protected int itemLayout;
protected String[] from;
protected int[] to;
public MyLoginListAdapter(Context context,
ArrayList<HashMap<String, Object>> list, int itemLayout,
String[] from, int[] to) {
super();
this.context = context;
this.list = list;
this.itemLayout = itemLayout;
this.from = from;
this.to = to;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return list.size();
}
@Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
class ViewHolder{
public ImageView userPhoto;
public TextView userQQ;
public ImageButton deleteButton;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder holder=null;
/*
currentPosition=position;
不能使用currentPosition,因为每绘制完一个Item就会更新currentPosition
这样得到的currentPosition将始终是最后一个Item的position
*/
if(convertView==null){
convertView=LayoutInflater.from(context).inflate(itemLayout, null);
holder=new ViewHolder();
holder.userPhoto=(ImageView)convertView.findViewById(to[0]);
holder.userQQ=(TextView)convertView.findViewById(to[1]);
holder.deleteButton=(ImageButton)convertView.findViewById(to[2]);
convertView.setTag(holder);
}
else{
holder=(ViewHolder)convertView.getTag();
}
holder.userPhoto.setBackgroundResource((Integer)list.get(position).get(from[0]));
holder.userQQ.setText((String)list.get(position).get(from[1]));
holder.deleteButton.setBackgroundResource((Integer)list.get(position).get(from[2]));
holder.deleteButton.setOnClickListener(new ListOnClickListener(position));
return convertView;
}
class ListOnClickListener implements OnClickListener{
private int position;
public ListOnClickListener(int position) {
super();
this.position = position;
}
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
list.remove(position);
//如果删除的就是当前显示的账号,那么将主界面当前显示的头像设置回初始头像
if(position==currentSelectedPosition){
currentUserImage.setImageResource(R.drawable.qqmain);
qqEdit.setText("");
currentSelectedPosition=-1;
}
else if(position<currentSelectedPosition){
currentSelectedPosition--; //这里小于当前选择的position时需要进行减1操作
}
listIndicatorButton.setBackgroundResource(R.drawable.indicator_down);
loginList.setVisibility(View.GONE); //让ListView列表消失,并且让游标向下指!
MyLoginListAdapter.this.notifyDataSetChanged();
}
}
}
}
另外再多附几张效果图: