Android开发中ListView 和 ScrollView 冲突如何解决

 更新时间:2016年9月20日 19:56  点击:1904
ListView 与 ScrollView 同在一界面会导致ListView 显示变形,ListView只显示出了一个条目的高度,本文我们来谈谈如何解决Android应用中ListView 和 ScrollView 共生的问题。

ListView 与 ScrollView 同在一界面会导致ListView 显示变形,ListView只显示出了一个条目的高度,本文我们来谈谈如何解决Android应用中ListView 和 ScrollView 共生的问题。


一开始就想着用一个ScrollView把主要内容和评论区的ListView包起来,然后添加各个控件的内容即可,但是写出来之后发现ListView只显示出了一个条目的高度,并且不能滑动,网上搜了一下发现原因是ScrollView和ListView都是可滑动的,把它们放在一块会有冲突,最后还是ScrollView获得了焦点,ListView不能滑动。网上的解决方法最多的是在加载ListView时用getMeasure计算每个条目和分割线的高度,然后相加,把结果设置为ListView控件的高度,不过貌似是只适用于ListView每个条目高度都一样的情况(没有试过,很奇怪为什么会这样)。要么就是自定义一个继承自ListView的控件,也是事先设置好ListView的高度,但这样总归比较麻烦,而且准确度不如由系统自己构造好。

懒癌发作实在不想自己去做这些事情,于是便想试一下比较投机的方法,就是在ListView的Adapter的getView方法中根据position构造不同的界面,即如果position是0,则用原来主要信息的xml文件取inflate convertView,否则就用评论条目的xml去inflate,经试验果然可行。之后不死心想看下有没有更好的实现方法,去overflow上找了一下,发现有人推荐的方法和我的差不多,所以认为这种方法是比较好的,不需要做额外的工作,只需要把inflate的工作由主Activity放在Adapter里就可以了。


getView方法


@Override
public View getView(int position, View convertView, ViewGroup parent)
{
    /*主信息界面*/
    if(0 == position)
    {
        MainHolder holder = null;
        convertView = inflater.inflate(R.layout.info, parent, false);
        holder = new MainHolder();
        convertView.setTag(holder);
                ······
                ······
    }
    /*评论界面*/
    else
    {
        ItemHolder holder = null;
        convertView = inflater.inflate(R.layout.item, parent, false);
        holder = new ItemHolder();
        convertView.setTag(holder);
        ······
        ······
        return convertView;
    }
}




另一个方法解决

Android 解决ListView 和 ScrollView 共存冲突的问题

ListView 与 ScrollView 同在一界面会导致ListView 显示变形,因为ListView 也有自带的滚动事件,故无法与ScrollView 相容,可能造成的现象是ListView 只能显示一行或者两行,其他数据在那一点儿宽的地方做滚动,甚不雅观。

下面是我的一个实现 步骤:

    1、继承LinearLayout,既然会冲突那就不用ListView 改成线性布局做动态布局效果
    2、继承BaseAdapter ,可以参照一下Android app源码中 Widget 目录下的SimpleAdapter 为前面扩展的LinearLayout做数据。
    3、模拟数据填充扩展后的BaseAdapter 为扩展后的LinearLayout 加载数据

第一步:新建LinearLayoutForListView 类使其扩展LinearLayout重写以下两个方法:

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public LinearLayoutForListView(Context context) {
    super(context);
}
public LinearLayoutForListView(Context context, AttributeSet attrs) {
    super(context, attrs);
    // TODO Auto-generated constructor stub
}




这两个方法可选,不过建议都写上,第一个方法可以让我们通过 编程的方式 实例化出来,第二个方法可以允许我们通过 XML的方式注册 控件,可以在第二个方法里面为扩展的复合组件加属性。

为其添加get / set 方法

/**
     * 获取Adapter
     * 
     * @return adapter
     */
    public AdapterForLinearLayout getAdpater() {
        return adapter;
    }
    /**
     * 设置数据
     * 
     * @param adpater
     */
    public void setAdapter(AdapterForLinearLayout adpater) {
        this.adapter = adpater;
        bindLinearLayout();
    }
    /**
     * 获取点击事件
     * 
     * @return
     */
    public OnClickListener getOnclickListner() {
        return onClickListener;
    }
    /**
     * 设置点击事件
     * 
     * @param onClickListener
     */
    public void setOnclickLinstener(OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }



 第二步:新建AdapterForLinearLayout 类继承自BaseAdapter,并为其添加构造函数

 

private LayoutInflater mInflater;
    private int resource;
    private List<? extends Map<String, ?>> data;
    private String[] from;
    private int[] to;
    public AdapterForLinearLayout(Context context,
            List<? extends Map<String, ?>> data, int resouce, String[] from,
            int[] to) {
        this.data = data;
        this.resource = resouce;
        this.data = data;
        this.from = from;
        this.to = to;
        this.mInflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }


 

此构造函数模仿 simpleAdapter 通过传进来的resouce 为布局设置数据。通过继承BaseAdapter 重要的实现方法在下面getView ,此方法判断通过传进来的 String[] from 与 int[] to 为分别查找出View 并为View 设置相应的Text,代码如下:

 

@Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        convertView = mInflater.inflate(resource, null);
        Map<String, ?> item = data.get(position);
        int count = to.length;
        for (int i = 0; i < count; i++) {
            View v = convertView.findViewById(to[i]);
            bindView(v, item, from[i]);
        }
        convertView.setTag(position);
        return convertView;
    }
    /**
     * 绑定视图
     * @param view
     * @param item
     * @param from
     */
    private void bindView(View view, Map<String, ?> item, String from) {
        Object data = item.get(from);
        if (view instanceof TextView) {
            ((TextView) view).setText(data == null ? "" : data.toString());
        }
    }



 Tip:

BindView 方法是一个自定义方法,在方法体内可以为通过判断使本类更具灵活性,如上,你不仅可以判断是TextView 并且可以传入任何你想要的View 只要在方法体内加入相应判断即可,数据可以通过data 做相应处理,具体如何操作读者可另行测试。

    convertView.setTag(position); 此句代码为View 设置tag 在以后我们可以通过 getTag 找出下标,后文有介绍如何通过下标操作数据。

下面是两个类的全部代码,读者可以无须更改直接使用:

LinearLayoutForListView
package com.terry.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
public class LinearLayoutForListView extends LinearLayout {
    private AdapterForLinearLayout adapter;
    private OnClickListener onClickListener = null;
    /**
     * 绑定布局
     */
    public void bindLinearLayout() {
        int count = adapter.getCount();
        for (int i = 0; i < count; i++) {
            View v = adapter.getView(i, null, null);
            v.setOnClickListener(this.onClickListener);
            if (i == count - 1) {
                LinearLayout ly = (LinearLayout) v;
                ly.removeViewAt(2);
            }
            addView(v, i);
        }
        Log.v("countTAG", "" + count);
    }
    public LinearLayoutForListView(Context context) {
        super(context);
    }
    public LinearLayoutForListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }
    /**
     * 获取Adapter
     * 
     * @return adapter
     */
    public AdapterForLinearLayout getAdpater() {
        return adapter;
    }
    /**
     * 设置数据
     * 
     * @param adpater
     */
    public void setAdapter(AdapterForLinearLayout adpater) {
        this.adapter = adpater;
        bindLinearLayout();
    }
    /**
     * 获取点击事件
     * 
     * @return
     */
    public OnClickListener getOnclickListner() {
        return onClickListener;
    }
    /**
     * 设置点击事件
     * 
     * @param onClickListener
     */
    public void setOnclickLinstener(OnClickListener onClickListener) {
        this.onClickListener = onClickListener;
    }
}
AdapterForLinearLayout
package com.terry.widget;
import java.util.List;
import java.util.Map;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class AdapterForLinearLayout extends BaseAdapter {
    private LayoutInflater mInflater;
    private int resource;
    private List<? extends Map<String, ?>> data;
    private String[] from;
    private int[] to;
    public AdapterForLinearLayout(Context context,
            List<? extends Map<String, ?>> data, int resouce, String[] from,
            int[] to) {
        this.data = data;
        this.resource = resouce;
        this.data = data;
        this.from = from;
        this.to = to;
        this.mInflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }
    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return data.size();
    }
    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return data.get(position);
    }
    @SuppressWarnings("unchecked")
    public String get(int position, Object key) {
        Map<String, ?> map = (Map<String, ?>) getItem(position);
        return map.get(key).toString();
    }
    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        convertView = mInflater.inflate(resource, null);
        Map<String, ?> item = data.get(position);
        int count = to.length;
        for (int i = 0; i < count; i++) {
            View v = convertView.findViewById(to[i]);
            bindView(v, item, from[i]);
        }
        convertView.setTag(position);
        return convertView;
    }
    /**
     * 绑定视图
     * @param view
     * @param item
     * @param from
     */
    private void bindView(View view, Map<String, ?> item, String from) {
        Object data = item.get(from);
        if (view instanceof TextView) {
            ((TextView) view).setText(data == null ? "" : data.toString());
        }
    }
}


对应的XML 如下:


<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView android:id="@+id/TextView01"
        android:layout_marginLeft="10px" android:textAppearance="?android:attr/textAppearanceLarge"
        android:layout_width="wrap_content" android:layout_height="wrap_content">
    </TextView>
    <TextView android:id="@+id/TextView02" android:layout_width="wrap_content"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:layout_marginLeft="10px" android:layout_height="wrap_content">
    </TextView>
    <View android:layout_height="1px" android:background="#FFFFFF"
        android:layout_width="fill_parent"></View>
</LinearLayout>


 

第三步:主页面使用控件并为其设置数据

XML如下:

<com.terry.widget.LinearLayoutForListView
                    android:orientation="vertical" android:layout_width="450px"
                    android:layout_height="fill_parent" android:id="@+id/ListView01">
                </com.terry.widget.LinearLayoutForListView>



加载数据如下:
    

lv = (LinearLayoutForListView) findViewById(R.id.ListView01);
    for (int i = 0; i < 10; i++) {
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("key_name", "name" + i);
        map.put("value_name", "value" + i);
        list.add(map);
    }
    final AdapterForLinearLayout Layoutadpater = new AdapterForLinearLayout(
            this, list, R.layout.test, new String[] { "key_name",
                    "value_name" }, new int[] { R.id.TextView01,
                    R.id.TextView02 });



     
事件操作,并通过下标得到数据源:
    

lv.setOnclickLinstener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            Toast.makeText(
                    BlueToothActivity.this,
                    Layoutadpater.get(Integer.parseInt(v.getTag()
                            .toString()), "key_name"), 1000).show();
        }
    });
    lv.setAdapter(Layoutadpater);

    

Tip:get方法是在Layoutadpater 封装的一个通过下标获取相应数据的方法请参考上文。

至此完成。有碰到这个问题的朋友可以试试。

Android开发中TextView控件是用来显示文本信息的,不过也可以显示图文信息,有时TextView满足不了我们的需要,需要我们去自定义,本文就来看看如何自定义TextView详。

Android控件中的TextView控件只有一个输入框,但是为了用于的操作方便我们应该实现一些功能:

1. 可以直接将内容删除的功能按钮

2. 可以记录用户以前输入的数据,同时能够将数据通过下拉显示,点击的时候实现输入

先上图:

01.jpeg


02.jpeg


下拉的图片没有做,所以和删除的图片使用同一个了,同志们可以直接在xml文件中更换就行了


分析:

肯定要使用自定义view来实现的,我们知道自定义view大概可以分为三类:自绘控件,组合控件,继承控件,我们这里是要进行增强的textView的功能,所以我这里使用的

是组合控件的方式来进行实现

既然使用组合控件,那么我们就来看看到底要使用什么控件来组合呢:

1.  其中一个必须是textView了

2. 下拉的那两个按钮是什么,当然是imageView了

3. 还有一个下拉列表,。。。。那就使用popwindow了

思路:

1. 如何实现直接删除用户的输入

使用addTextChangedListener监听textView内容的变化的时间根据内容的变化进行确定是否显示删除按钮,同时绑定删除按钮的点击事件

2.如何实现下拉显示用户输入过的数据,以及选中的时候实现输入

 我们通过下拉按钮的点击进行确定popwindow窗口的显示,在popwindow的界面有一个listview,在这个listview的adpater中进行绑定条目的点击事件

那么我们在adapter中绑定的事件,如何控制整个控件的功能输入呢,这里就是用handler了,在创建adapter的时候将handler传递过去,

然后当点击事件发生的时候我们使用handler进行send消息就行了,当然我们在send消息的时候将当前点击的数据传递过来就行了

上代码:

1. 控件主体代码

/** 
 * 自定义的控件,自带删除的按钮,下拉按钮 
 * @author zcs 
 * */  
public class EditTextClearSelect extends FrameLayout {  
      
    private EditText editText;  //显示的editText输入框  
    private ImageButton clearImageButton;  //显示的用于进行删除editText中内容的按钮  
    private ImageButton selectImageButton;  //显示的用于下拉editText中内容的按钮  
      
    //PopupWindow对象  ,用于已下拉形式显示数据  
    private PopupWindow selectPopupWindow= null;    
    //自定义Adapter    
    private ECS_OptionsAdapter optionsAdapter = null;    
    //下拉框选项数据源    
    private ArrayList<String> datas = new ArrayList<String>();     
    //下拉框依附组件   
    private LinearLayout parent;    
    //展示所有下拉选项的ListView    
    private ListView listView = null;     
    //用来处理选中或者删除下拉项消息    
    private Handler handler;    
      
    public EditTextClearSelect(Context context) {  
        super(context);  
    }  
    //用于对自定义的控件进行初始化  
    public EditTextClearSelect(Context context, AttributeSet attrs){  
        super(context, attrs);  
        //调用初始化自定义控件的方法  
        init(context,attrs);  
    }  
      
    /**   
     * 初始化下拉功能使用的组件   
     */    
    private void initWedget(Context context){    
        //初始化Handler,用来处理消息    
        handler = new Handler(){  
            public void handleMessage(Message msg) {  
                //当adapter中传递过来消息以后根据选中的id,将对应的值填写到EditText组件中  
                Bundle data = msg.getData();    
                //选中下拉项,下拉框消失    
                int selIndex = data.getInt("selIndex");    
                editText.setText(datas.get(selIndex));    
                dismiss();   
            }    
        };  
            
        //如果没有数据,则下拉菜单不显示  
        if( !(datas.size() > 0) ){  
            selectImageButton.setVisibility(View.GONE);  
        }  
          
        //设置点击下拉箭头图片事件,点击弹出PopupWindow浮动下拉框    
        selectImageButton.setOnClickListener(new View.OnClickListener() {    
            @Override    
            public void onClick(View v) {    
                 //获取下拉框依附的组件宽度,然后重新设置popWindow的宽度  
                selectPopupWindow.setWidth(parent.getWidth());  
                //显示PopupWindow窗口    
                popupWindwShowing();    
            }    
        });    
            
        //初始化PopupWindow    
        initPopuWindow(context);    
    }    
      
    /**   
     * 初始化PopupWindow   
     */     
    private void initPopuWindow(Context context){     
            
        //PopupWindow浮动下拉框布局    
        View loginwindow = LayoutInflater.from(context).inflate(R.layout.wecs_options, null);   
        listView = (ListView) loginwindow.findViewById(R.id.list);     
            
        //设置自定义Adapter    
        optionsAdapter = new ECS_OptionsAdapter(context,handler,datas);     
        listView.setAdapter(optionsAdapter);     
  
        selectPopupWindow = new PopupWindow(loginwindow, 0,LayoutParams.WRAP_CONTENT, true);     
        selectPopupWindow.setOutsideTouchable(true);     
        //实现当点击屏幕其他地方的时候将当前的pop关闭  
        selectPopupWindow.setBackgroundDrawable(new BitmapDrawable());      
    }    
      
    /**   
     * 显示PopupWindow窗口   
     * @param popupwindow   
     */     
    public void popupWindwShowing() {   
       //将pop窗口在自定义控件的底部显示  
       selectPopupWindow.showAsDropDown(parent);     
    }     
         
    /**   
     * PopupWindow消失   
     */     
    public void dismiss(){     
        selectPopupWindow.dismiss();     
    }    
      
     /** 
     * 初始化,包括增加删除按钮,下拉按钮 
     */  
    public void init(Context context,AttributeSet attrs){  
        //获取自定义控件的界面,相当于当前的自定义View就使用的View  
        View view = LayoutInflater.from(context).inflate(R.layout.weight_edit_clear_select, this, true);  
          
        parent =  (LinearLayout) view.findViewById(R.id.parent);  //当前的自定义控件  
        editText = (EditText) view.findViewById(R.id.et);  //输入框   
        clearImageButton = (ImageButton) view.findViewById(R.id.clear_ib); //删除按钮  
        selectImageButton = (ImageButton) view.findViewById(R.id.select_id); //下拉按钮  
          
        //当点击删除按钮的会后将输入框数据清空  
        clearImageButton.setOnClickListener(new OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                editText.setText("");  
            }  
        });  
        //根据输入框中的内容,决定是否显示删除按钮  
        editText.addTextChangedListener(new TextWatcher(){  
            @Override  
            public void onTextChanged(CharSequence s, int start, int before, int count) {  
                if (s.length() > 0) {  
                    //输入框有内容,显示按钮  
                    editText.setSelection(s.length());  
                    clearImageButton.setVisibility(View.VISIBLE);  
                } else {  
                    clearImageButton.setVisibility(View.GONE);  
                }  
            }  
            @Override  
            public void beforeTextChanged(CharSequence s, int start, int count,  
                    int after) {  
            }  
            @Override  
            public void afterTextChanged(Editable s) {  
            }  
  
        });  
          
        //初始化pop组件,设置下拉按钮的功能  
        initWedget(context);    
          
        //将属性值设置到控件中  
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.EditTextClearSelect);  
        //输入框的默认的显示文本  
        CharSequence text = a.getText(R.styleable.EditTextClearSelect_textECS);  
        CharSequence hint = a.getText(R.styleable.EditTextClearSelect_hintECS);  
        CharSequence parent_width  = a.getText(R.styleable.EditTextClearSelect_layout_width);  
          
        if (text!=null&&!"".equals(text.toString().trim())) {  
            editText.setText(text);  
            //设置光标位置  
            editText.setSelection(text.length());  
            this.clearImageButton.setVisibility(View.VISIBLE);  
        }  
        if (hint!=null&&!"".equals(hint.toString().trim())) {  
            editText.setHint(hint);  
        }  
        if(parent_width!=null && !"".equals(parent_width.toString().trim()) ){  
            //设置当前控件的宽度,为屏幕的百度比有参数进行设置  
            LayoutParams parent_lp = (LayoutParams) parent.getLayoutParams();  
            parent_lp.width = (int) (AppUtil.getScreenDispaly(context)[0] * ( (Double)(Double.parseDouble(parent_width.toString()) / 100) ));  
            Log.i("控件宽度", parent_lp.width+"");  
            parent.setLayoutParams(parent_lp);  
        }  
        a.recycle();  
    }  
       
    /** 
     * 获得输入的值 
     * @return 
     */  
    public String getText(){  
        return this.editText.getText().toString();  
    }  
       
    /** 
     * 设置值 
     * @param text 
     */  
    public void setText(String text){  
        this.editText.setText(text);  
    }  
       
    /** 
     * 设置默认值 
     * @param hint 
     */  
    public void setHint(String hint){  
        this.editText.setHint(hint);  
    }  
       
    /** 
     * 获得输入框控件 
     * @return 
     */  
    public EditText getEditText(){  
        return this.editText;  
    }  
       
    /** 
     * 获得消除按钮 
     * @return 
     */  
    public ImageButton getClearImageButton(){  
        return this.clearImageButton;  
    }  
      
    //设置下拉列表中的选项值  
    public void setOptionsValue(ArrayList<String> inDatas){  
        datas.clear();  
        if( (inDatas ==null) || !(inDatas.size() > 0) ){  
            selectImageButton.setVisibility(View.GONE);  
        }else{  
            selectImageButton.setVisibility(View.VISIBLE);  
            datas.addAll(inDatas);  
        }  
        optionsAdapter.notifyDataSetChanged();  
    }


2. popwindow里面listview的适配器

public class ECS_OptionsAdapter extends BaseAdapter {  
  
        private ArrayList<String> list = new ArrayList<String>();     
        private Context context = null;     
        //传递过来的hanler,用于进行通知操作(这里是通知自定义的view要继续修改editText中的数据)  
        private Handler handler;  
          
        public ECS_OptionsAdapter(Context context,Handler handler,ArrayList<String> list){    
            this.context = context;    
            this.handler = handler;    
            this.list = list;    
        }    
            
        @Override    
        public int getCount() {    
            return list.size();    
        }    
        
        @Override    
        public Object getItem(int position) {    
            return list.get(position);    
        }    
        
        @Override    
        public long getItemId(int position) {    
            return position;    
        }    
        
        @Override    
        public View getView(final int position, View convertView, ViewGroup parent) {    
                
            ViewHolder holder = null;     
            if (convertView == null) {     
                holder = new ViewHolder();     
                //下拉项布局    
                convertView = LayoutInflater.from(context).inflate(R.layout.wecs_option_item, null);     
                holder.textView = (TextView) convertView.findViewById(R.id.item_text);     
                convertView.setTag(holder);     
            } else {     
                holder = (ViewHolder) convertView.getTag();     
            }     
            holder.textView.setText(list.get(position));    
            //为下拉框选项文字部分设置事件,最终效果是点击将其文字填充到文本框    
            holder.textView.setOnClickListener(new View.OnClickListener() {    
                @Override    
                public void onClick(View v) {    
                    //当点击的时候进行发送消息,通知组件进行修改数据  
                    Message msg = new Message();    
                    //设置要传递的数据  
                    Bundle data = new Bundle();    
                    //设置选中索引    
                    data.putInt("selIndex", position);    
                    msg.setData(data);   
                    //发出消息    
                    handler.sendMessage(msg);  
                }    
            });    
            return convertView;     
        }    
        
    }    
  
    class ViewHolder {     
        TextView textView;     
    }


3. 使用

private EditTextClearSelect etcs;  
  
@Override  
protected void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.activity_main);  
    init();  
}  
  
//初始化  
private void init(){  
    String selectDatas = "admin,login,user";  
    etcs = (EditTextClearSelect) findViewById(R.id.username_edit);  
    //为控件设置下拉的数据  
    etcs.setOptionsValue( new ArrayList<String>(Arrays.asList(selectDatas.split(","))) );  
      
}



Android中自定义textview可以进行自体设置

面是代码:

package com.windy.androidfont;
import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.TextView;
public class FontTextView extends TextView {
    private Context mContext;
    private String TypefaceName = "";
    public String getTypefaceName() {
        return TypefaceName;
    }
    public void setTypefaceName(String typefaceName) {
        TypefaceName = typefaceName;
        Typeface typeface = Typeface.createFromAsset(mContext.getAssets(), "font/" + TypefaceName + ".ttf");
        this.setTypeface(typeface);
        System.gc();
    }
    public FontTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.mContext = context;
        int resouceId = attrs.getAttributeResourceValue(null, "typefaceName", 0);
        if (resouceId != 0) {
            TypefaceName = context.getResources().getString(resouceId);
        } else {
            TypefaceName = attrs.getAttributeValue(null, "typefaceName");
        }
        if (TypefaceName != null && !"".equals(TypefaceName)) {
            Typeface typeface = Typeface.createFromAsset(context.getAssets(), "font/" + TypefaceName + ".ttf");
            this.setTypeface(typeface);
        }
    }
    public FontTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        // 先判断是否配置的资源文件
        int resouceId = attrs.getAttributeResourceValue(null, "typefaceName", 0);
        if (resouceId != 0) {
            TypefaceName = context.getResources().getString(resouceId);
        } else {
            TypefaceName = attrs.getAttributeValue(null, "typefaceName");
        }
        if (TypefaceName != null && !"".equals(TypefaceName)) {
            Typeface typeface = Typeface.createFromAsset(context.getAssets(), "font/" + TypefaceName + ".ttf");
            this.setTypeface(typeface);
        }
    }
    public FontTextView(Context context) {
        super(context);
        this.mContext = context;
        // TypefaceName = attrs.getAttributeValue(null, "TypefaceName");
        if (TypefaceName != null && !"".equals(TypefaceName)) {
            Typeface typeface = Typeface.createFromAsset(context.getAssets(), "font/" + TypefaceName + ".ttf");
            this.setTypeface(typeface);
        }
    }
}


下面是布局:


<com.example.androidfont.FontTextView
        android:id="@+id/fontTextView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="69dp"
        typefaceName="pop"
        android:text="FontTextView"
        android:textSize="30sp" />


大字体是要设置的属性,pop是字体库的名字

注意:自体库文件我这里放到了assets中的font文件夹中给个截图吧:

strings.xml文件中的截图:可以把字体的配置放到字符串资源中,这样就能统一进行更改了,如果你的需要中要动态的进行配置的话,可以对FontTextView进行改写,我的想法是在将自体配置放进prefrence中进行配置,这样就可以直接在FontTextView中进行改写了,本来想把这个类写的完成呢,由于时间关系就没写,有需求的可以自己去实现。就说这么多吧。


Android自定义View的实现

 很多时候系统自带的View满足不了设计的要求,就需要自定义View控件。自定义View首先要实现一个继承自View的类。添加类的构造方法,override父类的方法,如onDraw,(onMeasure)等。如果自定义的View有自己的属性,需要在values下建立attrs.xml文件,在其中定义属性,同时代码也要做修改。

一个简单的例子:

·新建一个MyView类,继承自TextView,并添加构造方法:

package com.example.xhelloworld;
import android.content.Context;
import android.widget.TextView;
public class MyView extends TextView{
    public MyView(Context context) {
       super(context);
       // TODO Auto-generated constructor stub
    }
}


·再在主activity中调用。方法是setContentView(new MyView(this));这句

package com.example.xhelloworld;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
public class NewView extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_newview);
        setContentView(new MyView(this));
       
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_newview, menu);
        return true;
    }
}

运行后的结果为:

 


这样一个简单的自定义View就可以使用了。可以改变一下背景颜色,在MyView类中添加:

@Override
    protected void onDraw(Canvas canvas) {
       // TODO Auto-generated method stub
       super.onDraw(canvas);
       canvas.drawColor(Color.BLUE);
    }

即可完成。运行结果

 


上面的例子很简单,没有涉及到属性的添加。使用范围很小,不能在布局文件中使用。如果要在布局文件中用到,还需要添加一个构造方法:

public MyView(Context context,AttributeSet attrs){
       super(context, attrs);  
    }

当然,上面只是在code中做的修改,在xml文件(main.xml)中也需要进行如下操作:

<com.example.xhelloworld.NewView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />


至少在xml文件中写上上面的内容。其中com.example.xhelloworld.NewView这句是需要显示的控件所代表的类。Com.example.xhelloworld是类的包名,NewView是类名。这个类肯定是继承自View的自定义类(其实就是,使我们自己写的,这是废话了。。。),可以是在工程中直接源码添加xxxx.java的,也可以是在libs目录下自己新添加的jar包里面的。如果是jar包里面的一个类,则路径就是jar包里面,这个类的路径。

完成上面的两步之后就可以在代码中实例化这个布局文件了

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        //setContentView(new MyView(this));


显示的效果同上图。

下面介绍如何实现自定义View的属性设置。实现自定义View的属性设置,需要:

·在values目录下建立attrs.xml文件,添加属性内容

·在布局文件中添加新的命名空间xmlns,然后可以使用命名空间给自定义的空间设置属性

·设置完属性之后,当然还要对其进行处理。在自定义View类中的构造方法中进行处理

根据这三步给一个例子进行说明一下

首先添加attrs.xml文件,在定义属性

 

<resources> 
    <declare-styleable name="MyView"> 
    <attr name="textColor" format="color"/> 
    <attr name="textSize" format="dimension"/> 
    declare-styleable> 
resources>


然后在布局文件中完成:

xmlns:my=http://schemas.android.com/apk/res/com.example.xhelloworld
<com.example.xhelloworld.MyView 
       android:layout_width="fill_parent" 
       android:layout_height="wrap_content"   
       my:textColor="#FFFFFFFF"   
       my:textSize="22dp" 
    />


注:这步我在实现的时候出错,问题是显示找不到属性textColor和textSize,这奇怪的错误。解决方法是,在写my:textColor="#FFFFFFFF" 时,写到my之后,按alt+/,这是会自动添加一个xmlns,和my的路径是一样的,用生成的这个替换掉my就可以了。奇葩的问题就用奇葩的方法解决。起初我也不知道怎么弄,瞎搞出来的。

最后在MyView.java中添加另一个构造方法,并添加代码来处理从xml中获得的属性

public MyView(Context context,AttributeSet attrs){
       super(context, attrs);
         mPaint = new Paint();  
        //TypedArray是一个用来存放由context.obtainStyledAttributes获得的属性的数组  
        //在使用完成后,一定要调用recycle方法  
        //属性的名称是styleable中的名称+“_”+属性名称  
        //TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyView);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyView);
        int textColor = array.getColor(R.styleable.MyView_textColor, 0XFF00FF00); //提供默认值,放置未指定  
        float textSize = array.getDimension(R.styleable.MyView_textSize, 36);  
        mPaint.setColor(textColor);  
        mPaint.setTextSize(textSize);  
        array.recycle(); //一定要调用,否则这次的设定会对下次的使用造成影响  
      
    }


 完成之后就已经实现了自定视图的构造,自定义视图属性的添加很处理。现在完成的是一般的自定义视图,继承自TextView或者View等视图,也就是通过程序主UI线程完成更新的视图,如果是自定义SurfaceView,实现方法有所不同。

添加完之后肯定有很多疑问,自己去做可能还不能理解。这里再对上面操作进行解释说明。

背后的事

View类的构造方法:

·public view(Context context)       当在代码中创建对象时会被调用

·public View (Context context, AttributeSet attrs)

官方的文档是:

Constructor that is called when inflating a view from XML. This is called when a view is being constructed from an XML file, supplying attributes that were specified in the XML file. This version uses a default style of 0, so the only attribute values applied are those in the Context's Theme and the given AttributeSet

大致应该是这个方法是通过xml文件来创建一个view对象的时候调用。很显然xml文件一般是布局文件,就是现实控件的时候调用,而布局文件中免不了会有属性的设置,如android:layout_width等,对这些属性的设置对应的处理代码也在这个方法中完成。

两个参数

Context          The Context the view is running in, through which it can access the current theme, resources, etc.

Attrs              The attributes of the XML tag that is inflating the view

·public View (Context context, AttributeSet attrs,int defStyle)

Perform inflation from XML and apply a class-specific base style. This constructor of View allows subclasses to use their own base style when they are inflating. For example, a Button class's constructor would call this version of the super class constructor and supply R.attr.buttonStyle fordefStyle; this allows the theme's button style to modify all of the base view attributes (in particular its background) as well as the Button class's attributes.

看的不太懂,没用到,下放一下吧额

这就是为什么要添加上面的两个构造方法的原因。

进度条可以显示加载的进度,为手机应用提升了不少用户体验,本文我们来分享两个在Android应用开发中自定义的进度条实例。

先看看效果吧:

01.gif


这是个自定义的view,我这里就不去深究了,看demo界面的代码,看看如何使用的。

MainActivity.java

/** 
 * demo主界面 
 * @author jan 
 */  
public class MainActivity extends Activity implements OnProgressBarListener {  
      
    private Timer timer;  
    //进度条组件  
    private NumberProgressBar bnp;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        bnp = (NumberProgressBar) findViewById(R.id.numberbar1);  
        bnp.setOnProgressBarListener(this);  
        //创建一个定时任务,模拟进度更新  
        timer = new Timer();  
        timer.schedule(new TimerTask() {  
            @Override  
            public void run() {  
                runOnUiThread(new Runnable() {  
                    @Override  
                    public void run() {  
                        //递增progress数值  
                        bnp.incrementProgressBy(5);  
                    }  
                });  
            }  
        }, 1000, 100);  
    }  
  
    @Override  
    protected void onDestroy() {  
        super.onDestroy();  
        //最后别忘了取消定时任务  
        timer.cancel();  
    }  
    /** 
     * 实现对进度实时监听,max默认为100 
     */  
    @Override  
    public void onProgressChange(int current, int max) {  
        if (current == max) {  
            Toast.makeText(getApplicationContext(), getString(R.string.finish),  
                    Toast.LENGTH_SHORT).show();  
            bnp.setProgress(0);  
        }  
    }  
}


2.在我们的xml布局文件中加入他,别忘了加定义命名空间:xmlns:custom="http://schemas.android.com/apk/res-auto":

<com.daimajia.numberprogressbar.NumberProgressBar  
        android:id="@+id/numberbar1"  
        style="@style/NumberProgressBar_Default"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:padding="20dp"  
        custom:progress_current="0" />


3.他的style设定有以下属性:

<style name="NumberProgressBar_Default">  
        <item name="android:layout_height">wrap_content</item>  
        <item name="android:layout_width">match_parent</item>  
  
        <item name="progress_max">100</item>  
        <item name="progress_current">0</item>  
  
        <item name="progress_unreached_color">#CCCCCC</item>  
        <item name="progress_reached_color">#3498DB</item>  
  
        <item name="progress_text_size">14sp</item>  
        <item name="progress_text_color">#3498DB</item>  
  
        <item name="progress_reached_bar_height">6.5dp</item>  
        <item name="progress_unreached_bar_height">5.75dp</item>  
    </style>


4.再来看看他的源代码吧。

public class NumberProgressBar extends View {  
  
    private int mMaxProgress = 100;  
  
    /** 
     * Current progress, can not exceed the max progress. 
     */  
    private int mCurrentProgress = 0;  
  
    /** 
     * The progress area bar color. 
     */  
    private int mReachedBarColor;  
  
    /** 
     * The bar unreached area color. 
     */  
    private int mUnreachedBarColor;  
  
    /** 
     * The progress text color. 
     */  
    private int mTextColor;  
  
    /** 
     * The progress text size. 
     */  
    private float mTextSize;  
  
    /** 
     * The height of the reached area. 
     */  
    private float mReachedBarHeight;  
  
    /** 
     * The height of the unreached area. 
     */  
    private float mUnreachedBarHeight;  
  
    /** 
     * The suffix of the number. 
     */  
    private String mSuffix = "%";  
  
    /** 
     * The prefix. 
     */  
    private String mPrefix = "";  
  
  
    private final int default_text_color = Color.rgb(66, 145, 241);  
    private final int default_reached_color = Color.rgb(66, 145, 241);  
    private final int default_unreached_color = Color.rgb(204, 204, 204);  
    private final float default_progress_text_offset;  
    private final float default_text_size;  
    private final float default_reached_bar_height;  
    private final float default_unreached_bar_height;  
  
    /** 
     * For save and restore instance of progressbar. 
     */  
    private static final String INSTANCE_STATE = "saved_instance";  
    private static final String INSTANCE_TEXT_COLOR = "text_color";  
    private static final String INSTANCE_TEXT_SIZE = "text_size";  
    private static final String INSTANCE_REACHED_BAR_HEIGHT = "reached_bar_height";  
    private static final String INSTANCE_REACHED_BAR_COLOR = "reached_bar_color";  
    private static final String INSTANCE_UNREACHED_BAR_HEIGHT = "unreached_bar_height";  
    private static final String INSTANCE_UNREACHED_BAR_COLOR = "unreached_bar_color";  
    private static final String INSTANCE_MAX = "max";  
    private static final String INSTANCE_PROGRESS = "progress";  
    private static final String INSTANCE_SUFFIX = "suffix";  
    private static final String INSTANCE_PREFIX = "prefix";  
    private static final String INSTANCE_TEXT_VISIBILITY = "text_visibility";  
  
    private static final int PROGRESS_TEXT_VISIBLE = 0;  
  
  
    /** 
     * The width of the text that to be drawn. 
     */  
    private float mDrawTextWidth;  
  
    /** 
     * The drawn text start. 
     */  
    private float mDrawTextStart;  
  
    /** 
     * The drawn text end. 
     */  
    private float mDrawTextEnd;  
  
    /** 
     * The text that to be drawn in onDraw(). 
     */  
    private String mCurrentDrawText;  
  
    /** 
     * The Paint of the reached area. 
     */  
    private Paint mReachedBarPaint;  
    /** 
     * The Paint of the unreached area. 
     */  
    private Paint mUnreachedBarPaint;  
    /** 
     * The Paint of the progress text. 
     */  
    private Paint mTextPaint;  
  
    /** 
     * Unreached bar area to draw rect. 
     */  
    private RectF mUnreachedRectF = new RectF(0, 0, 0, 0);  
    /** 
     * Reached bar area rect. 
     */  
    private RectF mReachedRectF = new RectF(0, 0, 0, 0);  
  
    /** 
     * The progress text offset. 
     */  
    private float mOffset;  
  
    /** 
     * Determine if need to draw unreached area. 
     */  
    private boolean mDrawUnreachedBar = true;  
  
    private boolean mDrawReachedBar = true;  
  
    private boolean mIfDrawText = true;  
  
    /** 
     * Listener 
     */  
    private OnProgressBarListener mListener;  
  
    public enum ProgressTextVisibility {  
        Visible, Invisible  
    }  
  
    public NumberProgressBar(Context context) {  
        this(context, null);  
    }  
  
    public NumberProgressBar(Context context, AttributeSet attrs) {  
        this(context, attrs, R.attr.numberProgressBarStyle);  
    }  
  
    public NumberProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {  
        super(context, attrs, defStyleAttr);  
  
        default_reached_bar_height = dp2px(1.5f);  
        default_unreached_bar_height = dp2px(1.0f);  
        default_text_size = sp2px(10);  
        default_progress_text_offset = dp2px(3.0f);  
  
        //load styled attributes.  
        final TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.NumberProgressBar,  
                defStyleAttr, 0);  
  
        mReachedBarColor = attributes.getColor(R.styleable.NumberProgressBar_progress_reached_color, default_reached_color);  
        mUnreachedBarColor = attributes.getColor(R.styleable.NumberProgressBar_progress_unreached_color, default_unreached_color);  
        mTextColor = attributes.getColor(R.styleable.NumberProgressBar_progress_text_color, default_text_color);  
        mTextSize = attributes.getDimension(R.styleable.NumberProgressBar_progress_text_size, default_text_size);  
  
        mReachedBarHeight = attributes.getDimension(R.styleable.NumberProgressBar_progress_reached_bar_height, default_reached_bar_height);  
        mUnreachedBarHeight = attributes.getDimension(R.styleable.NumberProgressBar_progress_unreached_bar_height, default_unreached_bar_height);  
        mOffset = attributes.getDimension(R.styleable.NumberProgressBar_progress_text_offset, default_progress_text_offset);  
  
        int textVisible = attributes.getInt(R.styleable.NumberProgressBar_progress_text_visibility, PROGRESS_TEXT_VISIBLE);  
        if (textVisible != PROGRESS_TEXT_VISIBLE) {  
            mIfDrawText = false;  
        }  
  
        setProgress(attributes.getInt(R.styleable.NumberProgressBar_progress_current, 0));  
        setMax(attributes.getInt(R.styleable.NumberProgressBar_progress_max, 100));  
  
        attributes.recycle();  
        initializePainters();  
    }  
  
    @Override  
    protected int getSuggestedMinimumWidth() {  
        return (int) mTextSize;  
    }  
  
    @Override  
    protected int getSuggestedMinimumHeight() {  
        return Math.max((int) mTextSize, Math.max((int) mReachedBarHeight, (int) mUnreachedBarHeight));  
    }  
  
    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
        setMeasuredDimension(measure(widthMeasureSpec, true), measure(heightMeasureSpec, false));  
    }  
  
    private int measure(int measureSpec, boolean isWidth) {  
        int result;  
        int mode = MeasureSpec.getMode(measureSpec);  
        int size = MeasureSpec.getSize(measureSpec);  
        int padding = isWidth ? getPaddingLeft() + getPaddingRight() : getPaddingTop() + getPaddingBottom();  
        if (mode == MeasureSpec.EXACTLY) {  
            result = size;  
        } else {  
            result = isWidth ? getSuggestedMinimumWidth() : getSuggestedMinimumHeight();  
            result += padding;  
            if (mode == MeasureSpec.AT_MOST) {  
                if (isWidth) {  
                    result = Math.max(result, size);  
                } else {  
                    result = Math.min(result, size);  
                }  
            }  
        }  
        return result;  
    }  
  
    @Override  
    protected void onDraw(Canvas canvas) {  
        if (mIfDrawText) {  
            calculateDrawRectF();  
        } else {  
            calculateDrawRectFWithoutProgressText();  
        }  
  
        if (mDrawReachedBar) {  
            canvas.drawRect(mReachedRectF, mReachedBarPaint);  
        }  
  
        if (mDrawUnreachedBar) {  
            canvas.drawRect(mUnreachedRectF, mUnreachedBarPaint);  
        }  
  
        if (mIfDrawText)  
            canvas.drawText(mCurrentDrawText, mDrawTextStart, mDrawTextEnd, mTextPaint);  
    }  
  
    private void initializePainters() {  
        mReachedBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
        mReachedBarPaint.setColor(mReachedBarColor);  
  
        mUnreachedBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
        mUnreachedBarPaint.setColor(mUnreachedBarColor);  
  
        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);  
        mTextPaint.setColor(mTextColor);  
        mTextPaint.setTextSize(mTextSize);  
    }  
  
  
    private void calculateDrawRectFWithoutProgressText() {  
        mReachedRectF.left = getPaddingLeft();  
        mReachedRectF.top = getHeight() / 2.0f - mReachedBarHeight / 2.0f;  
        mReachedRectF.right = (getWidth() - getPaddingLeft() - getPaddingRight()) / (getMax() * 1.0f) * getProgress() + getPaddingLeft();  
        mReachedRectF.bottom = getHeight() / 2.0f + mReachedBarHeight / 2.0f;  
  
        mUnreachedRectF.left = mReachedRectF.right;  
        mUnreachedRectF.right = getWidth() - getPaddingRight();  
        mUnreachedRectF.top = getHeight() / 2.0f + -mUnreachedBarHeight / 2.0f;  
        mUnreachedRectF.bottom = getHeight() / 2.0f + mUnreachedBarHeight / 2.0f;  
    }  
  
    private void calculateDrawRectF() {  
  
        mCurrentDrawText = String.format("%d", getProgress() * 100 / getMax());  
        mCurrentDrawText = mPrefix + mCurrentDrawText + mSuffix;  
        mDrawTextWidth = mTextPaint.measureText(mCurrentDrawText);  
  
        if (getProgress() == 0) {  
            mDrawReachedBar = false;  
            mDrawTextStart = getPaddingLeft();  
        } else {  
            mDrawReachedBar = true;  
            mReachedRectF.left = getPaddingLeft();  
            mReachedRectF.top = getHeight() / 2.0f - mReachedBarHeight / 2.0f;  
            mReachedRectF.right = (getWidth() - getPaddingLeft() - getPaddingRight()) / (getMax() * 1.0f) * getProgress() - mOffset + getPaddingLeft();  
            mReachedRectF.bottom = getHeight() / 2.0f + mReachedBarHeight / 2.0f;  
            mDrawTextStart = (mReachedRectF.right + mOffset);  
        }  
  
        mDrawTextEnd = (int) ((getHeight() / 2.0f) - ((mTextPaint.descent() + mTextPaint.ascent()) / 2.0f));  
  
        if ((mDrawTextStart + mDrawTextWidth) >= getWidth() - getPaddingRight()) {  
            mDrawTextStart = getWidth() - getPaddingRight() - mDrawTextWidth;  
            mReachedRectF.right = mDrawTextStart - mOffset;  
        }  
  
        float unreachedBarStart = mDrawTextStart + mDrawTextWidth + mOffset;  
        if (unreachedBarStart >= getWidth() - getPaddingRight()) {  
            mDrawUnreachedBar = false;  
        } else {  
            mDrawUnreachedBar = true;  
            mUnreachedRectF.left = unreachedBarStart;  
            mUnreachedRectF.right = getWidth() - getPaddingRight();  
            mUnreachedRectF.top = getHeight() / 2.0f + -mUnreachedBarHeight / 2.0f;  
            mUnreachedRectF.bottom = getHeight() / 2.0f + mUnreachedBarHeight / 2.0f;  
        }  
    }  
  
    /** 
     * Get progress text color. 
     * 
     * @return progress text color. 
     */  
    public int getTextColor() {  
        return mTextColor;  
    }  
  
    /** 
     * Get progress text size. 
     * 
     * @return progress text size. 
     */  
    public float getProgressTextSize() {  
        return mTextSize;  
    }  
  
    public int getUnreachedBarColor() {  
        return mUnreachedBarColor;  
    }  
  
    public int getReachedBarColor() {  
        return mReachedBarColor;  
    }  
  
    public int getProgress() {  
        return mCurrentProgress;  
    }  
  
    public int getMax() {  
        return mMaxProgress;  
    }  
  
    public float getReachedBarHeight() {  
        return mReachedBarHeight;  
    }  
  
    public float getUnreachedBarHeight() {  
        return mUnreachedBarHeight;  
    }  
  
    public void setProgressTextSize(float textSize) {  
        this.mTextSize = textSize;  
        mTextPaint.setTextSize(mTextSize);  
        invalidate();  
    }  
  
    public void setProgressTextColor(int textColor) {  
        this.mTextColor = textColor;  
        mTextPaint.setColor(mTextColor);  
        invalidate();  
    }  
  
    public void setUnreachedBarColor(int barColor) {  
        this.mUnreachedBarColor = barColor;  
        mUnreachedBarPaint.setColor(mReachedBarColor);  
        invalidate();  
    }  
  
    public void setReachedBarColor(int progressColor) {  
        this.mReachedBarColor = progressColor;  
        mReachedBarPaint.setColor(mReachedBarColor);  
        invalidate();  
    }  
  
    public void setReachedBarHeight(float height) {  
        mReachedBarHeight = height;  
    }  
  
    public void setUnreachedBarHeight(float height) {  
        mUnreachedBarHeight = height;  
    }  
  
    public void setMax(int maxProgress) {  
        if (maxProgress > 0) {  
            this.mMaxProgress = maxProgress;  
            invalidate();  
        }  
    }  
  
    public void setSuffix(String suffix) {  
        if (suffix == null) {  
            mSuffix = "";  
        } else {  
            mSuffix = suffix;  
        }  
    }  
  
    public String getSuffix() {  
        return mSuffix;  
    }  
  
    public void setPrefix(String prefix) {  
        if (prefix == null)  
            mPrefix = "";  
        else {  
            mPrefix = prefix;  
        }  
    }  
  
    public String getPrefix() {  
        return mPrefix;  
    }  
  
    public void incrementProgressBy(int by) {  
        if (by > 0) {  
            setProgress(getProgress() + by);  
        }  
  
        if(mListener != null){  
            mListener.onProgressChange(getProgress(), getMax());  
        }  
    }  
  
    public void setProgress(int progress) {  
        if (progress <= getMax() && progress >= 0) {  
            this.mCurrentProgress = progress;  
            invalidate();  
        }  
    }  
  
    @Override  
    protected Parcelable onSaveInstanceState() {  
        final Bundle bundle = new Bundle();  
        bundle.putParcelable(INSTANCE_STATE, super.onSaveInstanceState());  
        bundle.putInt(INSTANCE_TEXT_COLOR, getTextColor());  
        bundle.putFloat(INSTANCE_TEXT_SIZE, getProgressTextSize());  
        bundle.putFloat(INSTANCE_REACHED_BAR_HEIGHT, getReachedBarHeight());  
        bundle.putFloat(INSTANCE_UNREACHED_BAR_HEIGHT, getUnreachedBarHeight());  
        bundle.putInt(INSTANCE_REACHED_BAR_COLOR, getReachedBarColor());  
        bundle.putInt(INSTANCE_UNREACHED_BAR_COLOR, getUnreachedBarColor());  
        bundle.putInt(INSTANCE_MAX, getMax());  
        bundle.putInt(INSTANCE_PROGRESS, getProgress());  
        bundle.putString(INSTANCE_SUFFIX, getSuffix());  
        bundle.putString(INSTANCE_PREFIX, getPrefix());  
        bundle.putBoolean(INSTANCE_TEXT_VISIBILITY, getProgressTextVisibility());  
        return bundle;  
    }  
  
    @Override  
    protected void onRestoreInstanceState(Parcelable state) {  
        if (state instanceof Bundle) {  
            final Bundle bundle = (Bundle) state;  
            mTextColor = bundle.getInt(INSTANCE_TEXT_COLOR);  
            mTextSize = bundle.getFloat(INSTANCE_TEXT_SIZE);  
            mReachedBarHeight = bundle.getFloat(INSTANCE_REACHED_BAR_HEIGHT);  
            mUnreachedBarHeight = bundle.getFloat(INSTANCE_UNREACHED_BAR_HEIGHT);  
            mReachedBarColor = bundle.getInt(INSTANCE_REACHED_BAR_COLOR);  
            mUnreachedBarColor = bundle.getInt(INSTANCE_UNREACHED_BAR_COLOR);  
            initializePainters();  
            setMax(bundle.getInt(INSTANCE_MAX));  
            setProgress(bundle.getInt(INSTANCE_PROGRESS));  
            setPrefix(bundle.getString(INSTANCE_PREFIX));  
            setSuffix(bundle.getString(INSTANCE_SUFFIX));  
            setProgressTextVisibility(bundle.getBoolean(INSTANCE_TEXT_VISIBILITY) ? Visible : Invisible);  
            super.onRestoreInstanceState(bundle.getParcelable(INSTANCE_STATE));  
            return;  
        }  
        super.onRestoreInstanceState(state);  
    }  
  
    public float dp2px(float dp) {  
        final float scale = getResources().getDisplayMetrics().density;  
        return dp * scale + 0.5f;  
    }  
  
    public float sp2px(float sp) {  
        final float scale = getResources().getDisplayMetrics().scaledDensity;  
        return sp * scale;  
    }  
  
    public void setProgressTextVisibility(ProgressTextVisibility visibility) {  
        mIfDrawText = visibility == Visible;  
        invalidate();  
    }  
  
    public boolean getProgressTextVisibility() {  
        return mIfDrawText;  
    }  
  
    public void setOnProgressBarListener(OnProgressBarListener listener){  
        mListener = listener;  
    }  
}


其实还是很简单的,没啥好说的了,有兴趣的可以下代码看看。




ndroid UI设计与开发 自定义进度条

国际惯例,先看效果图吧

01.jpeg


实现步骤一:先定义进度条的风格样式

<!-- 自定义进度条 -->
<style name="ProgressBar_Mini" parent="@android:style/Widget.ProgressBar.Horizontal">
    <item name="android:maxHeight">50dip</item>
    <item name="android:minHeight">8dip</item>
    <item name="android:indeterminateOnly">false</item>
    <item name="android:indeterminateDrawable">@android:drawable/progress_indeterminate_horizontal</item>
    <item name="android:progressDrawable">@drawable/progressbar_mini</item>
</style>


实现步骤二:定义样式style中用到的@drawable/progressbar_mini资源文件。可以再xml中绘制进度条的不同样式(圆角,直角……)和颜色。

<?xml version="1.0" encoding="utf-8"?>  
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >  
    <item android:id="@android:id/background">    
         <shape >    
                <corners android:radius="5dip" />    
                <gradient    
                    android:angle="270"    
                    android:centerY="0.75"    
                    android:endColor="#F5F5F5"    
                    android:startColor="#BEBEBE" />    
            </shape>    
    </item>    
    
    <item android:id="@android:id/secondaryProgress">    
    
        <clip >    
            <shape >    
                <corners android:radius="0dip" />    
                <gradient    
                    android:angle="270"    
                    android:centerY="0.75"    
                   android:endColor="#165CBC"    
                    android:startColor="#85B0E9" />    
            </shape>    
        </clip>    
    </item>    
    
    <item android:id="@android:id/progress">    
    
        <clip >    
            <shape >    
                <corners android:radius="5dip" />    
                <gradient    
                    android:angle="270"    
                    android:centerY="0.75"    
                   android:endColor="#165CBC"    
                    android:startColor="#85B0E9" />    
            </shape>    
              
        </clip>    
    </item>    
</layer-list>



实现步骤三:写java文件,定义进度条上显示的文字。

public class MyProgressBar extends ProgressBar {
    private String text_progress;
    private Paint mPaint;//画笔
    public MyProgressBar(Context context) {
        super(context);
        initPaint();
    }
    public MyProgressBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }
    public MyProgressBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initPaint();
    }
    
    @Override
    public synchronized void setProgress(int progress) {
        super.setProgress(progress);
        setTextProgress(progress);
    }
    @Override
    protected synchronized void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
        Rect rect=new Rect();
        this.mPaint.getTextBounds(this.text_progress, 0, this.text_progress.length(), rect);
        int x = (getWidth() / 2) - rect.centerX();
        int y = (getHeight() / 2) - rect.centerY();
        canvas.drawText(this.text_progress, x, y, this.mPaint);
    }
    /**
     * 
     *description: 初始化画笔
     *Create by lll on 2013-8-13 下午1:41:49
     */
    private void initPaint(){
        this.mPaint=new Paint();
        this.mPaint.setAntiAlias(true);
        this.mPaint.setColor(Color.WHITE);
    }
    private void setTextProgress(int progress){ 
        int i = (int) ((progress * 1.0f / this.getMax()) * 100);
        this.text_progress = String.valueOf(i) + "%";
    }
}


最后一步:xml文件中引用自定义的进度条:

 <com.example.widgetshop.MyProgressBar
    android:id="@+id/progressBar1"
    style="@style/ProgressBar_Mini"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:progress="100"
    android:layout_marginTop="24dp" />


OK,一个简单的更改背景和样式其带文字显示的进度条就完成。

本文我们总结了三种Android应用向用户发送提示信息的方法,分别是1)发送Toast信息;2)弹出对话框;3)发送通知。

Android应用向用户发送提示信息的三种方法是:

1)发送Toast信息

2)弹出对话框

3)发送通知


方式1:发送Toast信息:

这种方式最简单,在之前的学习中多次使用过。Toast是在一个浮动于应用之上的View中显示信息,显示一定的时间间隔后自动消失,不可获得焦点。

最简单的用法就是之前的学习中一直使用的:通过一个静态的makeText()方法返回一个Toast对象,然后调用show()方法。

如:

布局文件添加一个Button:

<Button

        android:id="@+id/button1"

        android:onClick="showToast"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_gravity="center_horizontal"

        android:layout_margin="20dp"

        android:text="@string/show_toast" />

MainActivity.java添加一个showToast()方法:

public void showToast(View view){

          Toast.makeText(this, "客观,您要的吐司来啦", Toast.LENGTH_LONG).show();

}

显示结果:

 


通常情况下,Toast.makeText()这个方法已然够用了,但是,如果有些别的需求,就需要看一下Toast的帮助信息了:

 


在Toast这个类中定义了两个常量:LENGTH_LONG和LENGTH_SHORT,用来表示Toast显示的时长。想让Toast停留的时间长一点,可以设置为LENGTH_LONG,否则设置为LENGTH_SHORT。

LENGTH_LONG的实际值为1,LENGTH_SHORT的实际值为0。在用到的地方也可以直接传入0或者1,但是阅读起来就显得不太直观了。

 


Toast中只定义了一个构造函数,传入一个上下文对象,返回一个空的Toast对象。

更常用的是,通过下面两个静态方法获取Toast对象:

 


这两个方法的区别在于第二个参数,该参数表示要显示的文本内容,第一个方法传入strings.xml中的一个字符串id,而第二个方法直接传入字符串对象,之前用到的都是第二个方法。下面测试下传入id的方法:

strings.xml中加入一个<string>节点:

<string name="tv_text">客官,这个是放在strings.xml的Toast,请慢用</string>

修改showToast()方法:

public void showToast(View view){

          Toast.makeText(this, R.string.tv_text, Toast.LENGTH_LONG).show();

}

运行结果:

 


如果直接使用Toast的构造方法获取一个Toast对象的话,该对象是一个空的Toast,这时,是不能直接show()的,毕竟没有任何要显示的内容。

如果:new Toast(this).show();

会报错:

 


提示,必须先调用setView():

 


当然,肯定有对应的getView():

 


这两个方法,分别用来给Toast设置要显示的View和取得当前Toast对象的View。

测试下setView()方法:

新建一个tv.xml布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:gravity="center_horizontal"

    android:orientation="vertical" >

    <TextView

        android:id="@+id/tv"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="@string/tv_text" />

 
    <Button

        android:id="@+id/btn1"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="Button" />

</LinearLayout>

修改showToast()方法:

public void showToast(View view){

          Toast toast = new Toast(this);

          View v = getLayoutInflater().from(this).inflate(R.layout.tv, null);

          toast.setView(v);

          toast.show();

}

显示效果:


 

直接把tv.xml中的内容都显示出来了,真丑……虽然显示有按钮,即便设置了事件监听,但是由于Toast是不能获取焦点的,所以点击是没有任何反应的。

在使用makeText()方法时,传入了三个参数,而构造方法只传入了与之对应的第一个表示上下文的参数,因而肯定有对应的setter方法:

 


setText()用于设置Toast中显示的信息,但是,注意到帮助文档中的信息:是用于修改之前使用makeText()创建的Toast对象显示的信息,如Toast对象是通过构造方法创建的是不能调用setText()方法的。

 


setDuration()用于设置显示的时长。

感觉还是挺鸡肋的,加起来都不如一个makeText()。

下面这个方法,可能看起来稍微有用些:

 



用来设置Toast的显示位置,如:

修改tv.xml设置个绿色背景,不要按钮了,太丑也没用:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:background="#00ff00"

    android:orientation="vertical" >

    <TextView

        android:id="@+id/tv"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="客官,您要的Toast来啦,请慢用" />

</LinearLayout>

修改showToast()方法:

public void showToast(View view){

          Toast toast = new Toast(this);

          toast.setView(getLayoutInflater().inflate(R.layout.tv, null));

          toast.setDuration(1);

          toast.setGravity(Gravity.CENTER, 0, 0);

          toast.show();

}

Gravity.CENTER表示的水平垂直均居中显示,后面的参数分别表示在第一个参数的基础上x轴方向上的偏移及y轴方向上的偏移,正数表示x轴上向右移动,y轴上向下移动;负数则表示x轴上向左移动,y轴上向上移动。这里指定为0,表示不偏移。

显示效果:

 

确实,居中显示了,而且是绿色背景,还是好丑,实际中应该不会这样用吧,费力不讨好。

总结:上面一大堆无用的东西,实际使用中直接使用makeText()方法获取Toast对象,需要修改显示位置,则调用setGravity()方法即可。实在需要自定义布局时再使用构造及setView()方法。

方式2:弹出对话框:

通过弹出对话框的形式向用户发送提示信息的方式,就需要用到AlertDialog这个类,准确来说,用的更多的是它的内部类AlertDialog.Builder类。

其实,弹出对话框的流程很简单:

1)获取一个AlertDialog.Builder对象:

AlertDialog.Builder builder = new Builder(this);

 

2)调用Builder的各种setXX方法,设置对话框的样式:

如:

builder.setTitle("俺是对话框");

builder.setMessage("没什么要说的");

3)显示对话框:

builder.show();

 

1.弹出一个简单对话框的示例:

修改布局文件,添加一个按钮,用于弹出对话框:

<Button

        android:id="@+id/button2"

        android:onClick="showDialog"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_gravity="center_horizontal"

        android:layout_margin="20dp"

        android:text="弹出对话框" />

在MainActivity中添加一个button2回调的方法:

public void showDialog(View view){

          AlertDialog.Builder builder = new Builder(this);

          builder.setTitle("俺是对话框");

          builder.setMessage("没什么要说的");

          builder.setPositiveButton("确定", new OnClickListener() {

                

                 @Override

                 public void onClick(DialogInterface dialog, int which) {

                      Toast.makeText(MainActivity.this, "你点了确定,啥也没有", 1).show();

                 }

           });

          builder.setNegativeButton("取消", new OnClickListener() {

                

                 @Override

                 public void onClick(DialogInterface dialog, int which) {

                      Toast.makeText(MainActivity.this, "取消啦", 1).show();

                 }

           });

          builder.show();

}

运行结果:

 


但是,此时如果按下Back键,该对话框就自动关闭了,一般我们需要的是用户或者按下确定或者按下取消,这时就可以设置按下Back不进行任何操作:

builder.setCancelable(false);

 

2.弹出一个单选框,修改showDialog()方法:

builder.setTitle("明天做什么");

final String[] items = new String[]{"看书","看电影","散步"};

builder.setSingleChoiceItems(items, -1, new OnClickListener() {

            

             @Override

             public void onClick(DialogInterface dialog, int which) {

                   // TODO Auto-generated method stub

                   Toast.makeText(MainActivity.this, "你选了"+items[which], 1).show();

                   dialog.dismiss();

             }

});

第一个参数是一个数组类型的单选项

第二个参数表示默认选定项目的下标,-1表示默认一个都不选

第三个参数指定选定项目的事件监听器。

dialog.dismiss();表示关闭对话框

显示效果:

 


3.弹出一个复选框,修改showDialog()方法:

builder.setTitle("哪个电影好看点");

      final String[] items = new String[]{"道士下山","大圣归来","战狼2","捉妖记","栀子花开"};

      builder.setMultiChoiceItems(items, new boolean[]{false,false,false,false,false}, new OnMultiChoiceClickListener() {

            

             @Override

             public void onClick(DialogInterface dialog, int which, boolean isChecked) {

                   // TODO Auto-generated method stub

                   Toast.makeText(MainActivity.this, "你觉得"+items[which]+(isChecked?"好看":"不好看"), 0).show();

             }

});

运行结果:

 


4.显示一个进度对话框:

ProgressDialog pd = new ProgressDialog(this);

pd.setTitle("正在缓冲……");

pd.setMessage("这网好烂啊……");

pd.show();

运行结果:

 


调用: pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 可以将进度对话框改为水平进度条的样式:


final ProgressDialog pd = new ProgressDialog(this);

      pd.setTitle("正在缓冲……");

      pd.setMessage("这网好烂啊……");

      pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

      pd.setMax(100);

      pd.show();

      new Thread(){

            public void run(){

                 try {

                        for (int i = 0; i < 100; i++) {

                              Thread.sleep(1000);

                              pd.setProgress(i);

                        }

                        pd.dismiss();

                   } catch (Exception e) {

                        e.printStackTrace();

                   }

            }

      }.start();

运行结果:

 


注意:此时点击屏幕对话框之外的部分,发现对话框消失了,解决方法:调用pd.setCancelable(false)。

方式3:发送通知:

当应用程序不在前台时,或者只有后台服务在运行,也许通过通知的方式向用户发送提示信息更方便一些:

要使用通知,涉及到的类有:NotificationManager和Notification这两个类。

见名知意,前一个用于管理通知,后一个就是被管理的通知实体类。

首先看管理器类:

NotificationManager要管理通知,所以有几个比较常用的方法:

首先要获取NotificationManager对象:

但是,查看帮助手册却没有发现它的构造方法……

文档中这样描述的:

 


所以,应当通过getSystemService(String)方法获取NotificationManager对象。

这个方法时Context中的方法,查看帮助手册发现:

 


Context中定义了好几个String类型的常量,其中就有NOTIFICATION_SERVICE,后面的描述信息更是直接说明通过将这个字符串常量传给getSystemService()可以获取NotificationManager实例。

 


但是getSystemService()方法返回的是Object对象,故而,需要强制类型转换:

NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

 


notify()方法用于发出一个通知,cancel()方法由于取消通知。

有了通知管理器,要让它发送通知,还是需要有通知实体的:

 


构造方法参数说明:

第一个参数用来指定通知在状态栏的图标

第二个参数用来指定通知在状态栏的描述

第三个参数用来指定什么时候显示

如:

Notification notification = new Notification(R.drawable.ic_launcher, "这是一个通知", System.currentTimeMillis());

 


Notification中的方法就这几个,最有用的只有setLatestEventInfo()方法

 


用于给通知在任务栏中的显示设置一个标准的布局

参数说明:

第一个参数指定上下文

第二个参数指定在任务栏中显示的标题

第三个参数指定在任务栏中显示的内容

第四个参数是PendingIntent类型的,是一种特殊的意图,用于指定通知被点击之后的动作,若设置为空则表示不进行任何处理。

一个简单的通知示例:

在布局文件中添加第三个按钮,用于发送通知:

<Button

        android:id="@+id/button3"

        android:onClick="showNotification"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_gravity="center_horizontal"

        android:layout_margin="20dp"

        android:text="弹出通知" />

在MainActivity中添加一个showNotification()方法,用于按钮的回调:

public void showNotification(View view){

          NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

          Notification notification = new Notification(R.drawable.ic_launcher, "这是一个通知", System.currentTimeMillis());

          notification.setLatestEventInfo(this, "这是个通知", "没有通知内容", null);

          manager.notify(123, notification);

}

运行效果:

 


查看任务栏:

 


但是,点击这个通知是没有任何反应的。

因为notification.setLatestEventInfo(this, "这是个通知", "没有通知内容", null);第四个参数设置为null了。

下面稍微了解下关于第四个参数的内容:

PendingIntent:延迟意图,是一种特殊的意图,这种意图并不是在发出之后立即得到执行,而是交由其他应用在恰当的时候进行执行处理。

PendingIntent实例的获得可以通过以下几个静态方法:

getActivity(Context, int, Intent, int)返回一个PendingIntent实例用于启动一个新的Activity。

getBroadcast(Context, int, Intent, int) 返回一个PendingIntent实例用于发出广播

getService(Context, int, Intent, int) 返回一个PendingIntent实例用于启动一个新的Service。

它们均需要传入一个上下文对象,及一个普通的Intent对象。

下面获取一个PendingIntent对象,用来打开一个新的Activity,然后将其传入notification.setLatestEventInfo()方法:

新建一个Actvity:

public class NotificationActivity extends Activity {

 

      @Override

      protected void onCreate(Bundle savedInstanceState) {

           super.onCreate(savedInstanceState);

           setContentView(R.layout.tv);

      }

}

布局文件:


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:background="#00ff00"

    android:orientation="vertical" >

    <TextView

        android:id="@+id/tv"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="客官,您要的通知来啦,请细阅" />

</LinearLayout>


修改showNotification()方法:

public void showNotification(View view){

          NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

          Notification notification = new Notification(R.drawable.ic_launcher, "这是一个通知", System.currentTimeMillis());

          Intent intent = new Intent(this, NotificationActivity.class);

          PendingIntent pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);

          notification.setLatestEventInfo(this, "这是个通知", "没有通知内容", pi);

          manager.notify(123, notification);

}

运行结果:

点击通知:

 


Notification中更多的是设置通知的各个属性,来更改通知的类型:

 


这些字段都是共有的,可以直接赋值:

如:notificaion.icon设置通知的图标

notification.sound设置通知的铃音

notification.vibrate设置通知的震动模式,需要赋值为一个long的数组

如notification.vibrate = new long[]{0,1000,500,1000,500};下标为偶数的表示静止时间,下标为奇数的表示震动时间,单位为毫秒

ledARGB、ledOffMS、ledOnMS可以用来通知到来时led灯的显示方式

这些都可以自己测试使用下,这里不一一列举了。

此外,新版本中可以采用与对话框类似的Builder内部类方式,创建Notification对象那个,并设置其属性,但出于兼容性的考虑,这里没有列举:

简单示例如下:

Notification notification = new Notification.Builder(this)

          .setSmallIcon(R.drawable.ic_launcher).setContentTitle("这是个通知")

          .setContentText("没有通知内容").getNotification();

          manager.notify(123, notification);

以上,就是三种向用户发送提示信息的三种方式。

本教程我们来讲讲在android开发经常用到的简单控件,android自定义控件简单示例,ListView控件的简单使用。

基本控件


1.TextView:

功能与传统的桌面应用开发中的Label控件相似,用于显示文本信息

如:

<TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#0000ff"
        android:textSize="40sp"
        android:text="@string/hello_world" />


显示效果:


上面的xml代码中,设置了几个常用的属性:

android:layout_width和android:layout_height分别设置控件的宽高

textColor设置显示的文本的颜色

textSize设置显示的文本的字体大小

text设置显示的文本内容。

2.Button:

前面用到的比较多,经常被用到的就是通过id获取按钮,然后绑定单击监听事件,这里仅列举个例子:

activity_main.xml:

<TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#0000ff"
        android:textSize="40sp"
        android:text="@string/hello_world" />
    <Button
        android:id="@+id/btn"
        android:layout_below="@id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/btnText"/>


MainActivity.java:

public class MainActivity extends ActionBarActivity {
 
    private TextView tv;
      private Button btn;
 
      @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       
        btn = (Button) findViewById(R.id.btn);
        tv = (TextView) findViewById(R.id.tv);
        btn.setOnClickListener(new OnClickListener() {
                
                 @Override
                 public void onClick(View v) {
                      // TODO Auto-generated method stub
                      tv.setText("It's changed!");
                 }
           });
    }
}


3.EditText:

即文本输入框,如下修改程序,在按钮之上添加一个EditText,点击按钮,会获取EditText的值并把它设置为TextView的Text属性:

activity_main.xml:

<TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#0000ff"
        android:textSize="40sp"
        android:text="@string/hello_world" />
    <EditText
        android:id="@+id/et"
        android:layout_below="@id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="@string/hintText"
        />
      <Button
        android:id="@+id/btn"
        android:layout_below="@id/et"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/btnText"/>


MainActivity.java:

public class MainActivity extends ActionBarActivity {
    private TextView tv;
      private Button btn;
      private EditText et;
 
      @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       
        btn = (Button) findViewById(R.id.btn);
        tv = (TextView) findViewById(R.id.tv);
        et = (EditText)findViewById(R.id.et);
        btn.setOnClickListener(new OnClickListener() {
                
                 @Override
                 public void onClick(View v) {
                      // TODO Auto-generated method stub
                      Editable text = et.getText();
                      tv.setText(text.toString());
                 }
           });
    }
}


运行效果:

 
输入值,然后点击按钮:

 


注意到由于EditText的layout_height属性是wrap_content,所以会随着输入内容的增多不断变大,影响整体布局。若想固定其高度,可以设置maxLines属性,设置最多只显示的行数,其他内容向上滚动

如:android:maxLines = “1”

EditText的高度就不会变化了。

4.ImageView:

使用来显示图片的一个控件,之前的程序中曾经用到过,当然,它最主要的属性肯定是要显示图片的来源了,即android:src属性,将要显示的图片存放在res/drawable中,如图片名为hero.png。要显示该图片,设置android:src=”@drawable/hero”即可。

<ImageView
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/hero"/>


显示结果:



5.ProgressBar:

即进度条,使用style属性,可以设置不同的显示风格:

1)不设置style属性或者设置为style="?android:attr/progressBarStyle" ,环形显示

 
2)style="?android:attr/progressBarStyleHorizontal",水平横条显示

 


3)style="?android:attr/progressBarStyleLarge",大号的环形显示

 


4)style="?android:attr/progressBarStyleSmall",小号的

 


进度条当然是用来显示进度的,通过findViewById()获取ProgressBar,然后使用setProgress()就可以设置当前进度,使用getProgress()可以获取当前进度。

如:

布局代码:

<ProgressBar
          android:id="@+id/pb"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          style="?android:attr/progressBarStyleHorizontal"
          android:max="100"
          />
      <Button
          android:id="@+id/btn"
          android:layout_below="@id/pb"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@string/add_progress"/>


Activity代码:

public class MainActivity extends ActionBarActivity {
    private ProgressBar pb;
      private Button btn;
      @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        pb = (ProgressBar) findViewById(R.id.pb);
        btn = (Button) findViewById(R.id.btn);
        Log.i("PB",pb.getProgress()+"");
        btn.setOnClickListener(new OnClickListener() {
                
                 @Override
                 public void onClick(View v) {
                      // TODO Auto-generated method stub
                      Log.i("PB",pb.getProgress()+"");
                      pb.setProgress(pb.getProgress()+10);
                 }
           });
    }
}


运行结果:

 


初始时,默认进度为0



多次点击按钮之后:

 


达到android:max所设置的最大值后,再加也不会有变化了。

6.AlertDialog:

这个控件就是弹出一个对话框,类似于桌面开发中的模态对话框,必须关闭该对话框,才能进行后续交互操作,可用于显示比较重要的内容。

AlertDialog的构造方法都是protected,没法直接通过构造来创建AlertDialog,但是可以通过其内部类Builder来创建。

具体使用可以参考帮助手册中关于这个内部类的帮助信息,下面举个简单例子:

AlertDialog.Builder dialog = new AlertDialog.Builder(this);
        dialog.setTitle("Warning");
        dialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                
                 @Override
                 public void onClick(DialogInterface dialog, int which) {
                      // TODO Auto-generated method stub
                     
                 }
           });
        dialog.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
                
                 @Override
                 public void onClick(DialogInterface dialog, int which) {
                      // TODO Auto-generated method stub
                     
                 }
           });
        dialog.setMessage("warning, hahaha");
        dialog.show();


运行结果:

 


7.ProgressDialog:

类似于AlertDialog,也是对话框,不过它显示的内容是一个进度条,好像是对话框和进度条两个控件的结合。

ProgressDialog pd = new ProgressDialog(this);

pd.setTitle("Data Loading...");

pd.show();

运行结果:




自定义控件简单示例

有时候,可能觉得系统提供的控件太丑,就会需要自定义控件来实现自己想要的效果。

以下主要参考《第一行代码》

1.自定义一个标题栏:

系统自带的标题栏很丑,且没什么大的作用,所以我们之前会在onCreate()中调用requestWindowFeature(Window.FEATURE_NO_TITLE);设置不显示标题栏。

下面自定义一个标题栏,中间显示标题,左右各有一个按钮:

title.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"
    android:background="#bbbbbb" >
    <Button
        android:id="@+id/btn_back"
        android:text="@string/back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:layout_gravity="left|center_vertical"
        android:textColor="#0099cc"
        android:layout_weight="1"/>
    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:textColor="#0099cc"
        android:text="@string/this_is_title"
        android:layout_gravity="center"
        android:gravity="center"
        android:layout_weight="2"/>
    <Button
        android:id="@+id/btn_edit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/edit"
        android:layout_margin="5dp"
        android:layout_gravity="right|center_vertical"
        android:textColor="#0099cc"
        android:layout_weight="1"/>
</LinearLayout>


Activity代码:

protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);     
           requestWindowFeature(Window.FEATURE_NO_TITLE);
           setContentView(R.layout.title);
}

运行结果:

 


2.复用布局代码:

想让这个标题栏应用在以后的每个布局文件,要怎么做呢?

总不能每次都把这些xml代码重写一遍吧。

android布局中提供了类似于c预处理指令#include的<include>标签,可以实现布局代码的复用。

下面新建一个first_layout.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="match_parent"
    android:orientation="vertical" >
    <include layout="@layout/title"/>
    <Button android:id="@+id/btn"
        android:text="@string/i_m_a_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"/>
</LinearLayout>


修改setContentView(R.layout.first_layout);

显示结果:

 


现在Back和Edit按钮都没有任何事件处理的,怎样实现点击Back按钮就结束当前Activity呢?方法跟之前的做法完全一样,使用findViewById()根据id找到Back按钮,然后设置click事件监听即可。

代码如下:

public class FirstActivity extends Activity {
      @Override
      protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);     
           requestWindowFeature(Window.FEATURE_NO_TITLE);
           setContentView(R.layout.first_layout);
           Button btn = (Button) findViewById(R.id.btn_back);
           btn.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
                      // TODO Auto-generated method stub
                      FirstActivity.this.finish();
                 }
           });
      }
}


布局文件的复用已然通过<include>实现了,但是每次都要重新写事件监听,还是觉得麻烦……到这里一般就会想到抽象出一个自定义类,每次需要的时候,直接使用该自定义类不就行了,其实就是自定义控件的做法了。

3.自定义控件,复用功能代码

TitleLinearLayout.java代码:

public class TitleLinearLayout extends LinearLayout {
      public TitleLinearLayout(Context context, AttributeSet attrs) {
           super(context, attrs);
           LayoutInflater.from(context).inflate(R.layout.title, this);
           Button btn_back = (Button) findViewById(R.id.btn_back);
           btn_back.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
                      // TODO Auto-generated method stub
                      Log.i("clicked","back");
                      ((Activity)getContext()).finish();
                 }
           });
      }
}


继承自LinearLayout,实现带两个参数的构造方法。在构造方法中,加载布局文件,并对其中的Back按钮进行事件监听设置。

LayoutInflater.from(context).inflate(R.layout.title, this);用于动态加载布局文件。

注意到,Activity中有一个获取LayoutInflater的方法,所以,也可以使用下面一行代码加载布局文件:

((Activity)context).getLayoutInflater().inflate(R.layout.title, this);这种方法,在Activity代码中比较常用,而这里需要进行类型强转,反倒麻烦点,而且不如第一个方法安全。

如何使用自定义的控件呢?

first_layout代码如下:

<?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="match_parent"
    android:orientation="vertical" >
    <cn.csc.custom_ui.TitleLinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </cn.csc.custom_ui.TitleLinearLayout>
    <Button android:id="@+id/btn"
        android:text="@string/i_m_a_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"/>
</LinearLayout>


说明:

1)在布局文件中,引用自定义的控件,需要使用完整的类限定名,即包名.类名的方式;

2)在定义控件中,设置属性时,使用alt+/进行代码提示补全功能将经常不可用,标签名可以先设置为内置控件,然后进行属性的设置,之后再把标签名改回到自定义的控件的完整限定名即可。



控件ListView的简单使用

MVC模式:

MVC的基本原理就是通过Controller连接View和Model。当View中所显示的数据发生变化时,会通知Controller,然后由Controller调用Model中的相关方法执行相应的数据修改操作。反之,当Model中的数据发生变化时,也会通知Controller,由Controller通知View更新显示内容。如此一来,就使得数据部分与视图部分相分离,任何一方发生改变都不会影响到另一方。

而在android中,MVC的一个常见应用就是ListView显示数据。V代表的就是显示控件;M代表的是各种数据源,可以是自己定义的List或者数组,也可以是数据库,文件等;C代表的是Adapter类,android中比较常见的adapter有:BaseAdapter,ArrayAdapter,SimpleAdapter等。

ListView通过setAdapter方法实现了其和一个Adapter对象的绑定,Adapter一般通过getView()方法返回当前列表项所要显示的View对象,完成了对Model中数据的读取。

当Model发生变化时,会调用BaseAdapter.notifyDataSetChanged()方法通知组件数据已然变化,此时Adapter就会调用getView()方法重新显示组件内容。

ListView:间接继承自抽象类AdapterView。

常见方法:

void  setOnItemClickListener(AdapterView.OnItemClickListener listener)

void  setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener)

void  setAdapter(ListAdapter adapter)

Adapter:是一个接口

主要的方法:

abstract int  getCount()  返回要显示的item总数

abstract Object  getItem(int position)  根据item索引返回该item

abstract long  getItemId(int position)  返回item的id。

abstract View  getView(int position, View convertView, ViewGroup parent)返回一个用来展示数据源中索引为position的View对象。

BaseAdapter是一个间接实现了Adapter接口的抽象类

自定义Adapter继承BaseAdapter时,需要提供上面四个方法的实现。主要要实现的是getCount()和geView()方法。

ArrayAdapter<T>, CursorAdapter, SimpleAdapter则直接继承自BaseAdapter,实现了其抽象方法。

ListView示例1:展示一个字符串数组中的各个字符串。

main_layout.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="match_parent"
    android:orientation="vertical" >
    <ListView
        android:id="@+id/listview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
</LinearLayout>


MainActivity.java:

public class MainActivity extends Activity {
     
      private String[] strs;
      @Override
      protected void onCreate(Bundle savedInstanceState) {
           // TODO Auto-generated method stub
           super.onCreate(savedInstanceState);
           setContentView(R.layout.main_layout);
           strs = new String[]{"aaa","bbb","ccc","ddd","eee","fff","ggg","hhh","iii"};
           ListView lv = (ListView) findViewById(R.id.listview);
           lv.setAdapter(new MyBaseAdapter());
          
      }
      class MyBaseAdapter extends BaseAdapter{
           @Override
           public int getCount() {
                 return strs.length;
           }
           @Override
           public Object getItem(int position) {
                 // TODO Auto-generated method stub
                 return null;
           }
           @Override
           public long getItemId(int position) {
                 return 0;
           }
           @Override
           public View getView(int position, View convertView, ViewGroup parent) {
                 TextView tv = null;
                 tv = new TextView(MainActivity.this);
                 Log.i("listview", position+"get view");
                 tv.setText(strs[position]);
                 tv.setTextSize(40);
                 tv.setTextColor(Color.RED);
                 return tv;
           }
      }
}

运行结果:

 


注意到,每次滚动屏幕显示新的item,或将滚出屏幕上方的item重新显示出来都会调用getItem()方法。而上面的代码中每次都会新建一个TextView,这样做是存在问题的。

getView(int position, View convertView, ViewGroup parent),其中参数convertView在允许的情况下,会保存旧的View实例,以便拿来复用。所以,可以利用该参数来优化上面的getView()实现。

代码修改如下:

public View getView(int position, View convertView, ViewGroup parent) {
 
                 TextView tv = null;
                 Log.i("listview", position+"get view");
                 if(convertView == null){
                      tv = new TextView(MainActivity.this);
                 }
                 else{
                      tv = (TextView) convertView;
                 }
                 tv.setText(strs[position]);
                 tv.setTextSize(40);
                 tv.setTextColor(Color.RED);
                 return tv;
}


其实,若只是显示数组中的内容,直接使用ArrayAdapter会比较方便:

MainActivity.java修改如下:

private String[] strs;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
       // TODO Auto-generated method stub
       super.onCreate(savedInstanceState);
       setContentView(R.layout.main_layout);
       strs = new String[]{"aaa","bbb","ccc","ddd","eee","fff","ggg","hhh","iii"};
       ListView lv = (ListView) findViewById(R.id.listview);
       ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, strs);
       lv.setAdapter(adapter);
      
  }


运行结果:

 


其中android.R.layout.simple_list_item_1是系统自带的一个布局id。

一般来说,在ListView中显示的东西可能更加复杂点,只靠一个TextView肯定解决不了,这时,就可以为ListView中的item根据需要编写一个布局文件。

如:item_layout.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/hero"
        />
    <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="35sp"
        />
</LinearLayout>


用于在线性布局中水平显示一个图片和一个字符串。

修改MyAdapter内部类的getView():

public View getView(int position, View convertView, ViewGroup parent) {
 
         View view = null;
         Log.i("listview", position+"get view");
         if(convertView == null){
              view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_layout, null);
         }
         else{
              view =  convertView;
         }
         TextView tv = (TextView) view.findViewById(R.id.tv_name);
         tv.setText(strs[position]);
         return view;
   }


运行结果:

 

另一种可能常用的Adapter是SimpleAdapter。

构造函数:SimpleAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to)

context指定上下文

data指定存放数据的list对象,list中存放的是Map对象。

resource指定item的布局文件id

from和to是有序的,两者中的元素必须一一对应,from中存放的是list中每个map对象中的key值,to存放的是显示对应key获取的值的控件的id。

如:item_layout.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >
    <ImageView
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
    <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="35sp"
        />
</LinearLayout>

MainActivity.java中修改如下:

protected void onCreate(Bundle savedInstanceState) {
           // TODO Auto-generated method stub
           super.onCreate(savedInstanceState);
           setContentView(R.layout.main_layout);
           ListView lv = (ListView) findViewById(R.id.listview);
           List<Map<String,Object>> data = new ArrayList<Map<String,Object>>();
           Map<String,Object> map = new HashMap<String, Object>();
           map.put("img", R.drawable.hero);
           map.put("str", "aaa");
           data.add(map);
           map = new HashMap<String, Object>();
           map.put("img", R.drawable.hero);
           map.put("str", "bbb");
           data.add(map);
           map = new HashMap<String, Object>();
           map.put("img", R.drawable.hero);
           map.put("str", "ccc");
           data.add(map);
           map = new HashMap<String, Object>();
           map.put("img", R.drawable.hero);
           map.put("str", "ddd");
           data.add(map);
           map = new HashMap<String, Object>();
           map.put("img", R.drawable.hero);
           map.put("str", "eee");
           data.add(map);
          
           SimpleAdapter sa = new SimpleAdapter(this, data, R.layout.item_layout, new String[]{"img","str"}, new int[]{R.id.iv,R.id.tv_name});
           lv.setAdapter(sa);
}

显示结果:

 


关于ListView的事件监听:

AdapterView中定义了几个设置事件监听的方法,如:

void  setOnItemClickListener(AdapterView.OnItemClickListener listener)

void  setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener)

ListView间接继承了AdapterView,所以,要处理事件监听时,可以调用ListView中相关的方法。

注:如果调用了ListView的setOnClickListener()方法,会报如下错误:

java.lang.RuntimeException: Don't call setOnClickListener for an AdapterView. You probably want setOnItemClickListener instead

setOnItemClickListener()方法示例:

ListView lv = (ListView) findViewById(R.id.listview);
   lv.setOnItemClickListener(new OnItemClickListener() {
         @Override
         public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
              Log.i("listview","onClick");
              Log.i("listview",view.toString());
              Log.i("listview",position+"");
              Log.i("listview",id+"");
              TextView tv = (TextView) view.findViewById(R.id.tv_name);
              Toast.makeText(MainActivity.this, tv.getText(), Toast.LENGTH_LONG).show();
         }
   });


运行结果:

 


[!--infotagslink--]

相关文章

  • Android子控件超出父控件的范围显示出来方法

    下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
  • Android开发中findViewById()函数用法与简化

    findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20
  • Android模拟器上模拟来电和短信配置

    如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
  • 夜神android模拟器设置代理的方法

    夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
  • android自定义动态设置Button样式【很常用】

    为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
  • Android WebView加载html5页面实例教程

    如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
  • 深入理解Android中View和ViewGroup

    深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20
  • Android自定义WebView网络视频播放控件例子

    下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
  • Android用MemoryFile文件类读写进行性能优化

    java开发的Android应用,性能一直是一个大问题,,或许是Java语言本身比较消耗内存。本文我们来谈谈Android 性能优化之MemoryFile文件读写。 Android匿名共享内存对外A...2016-09-20
  • Android设置TextView竖着显示实例

    TextView默认是横着显示了,今天我们一起来看看Android设置TextView竖着显示如何来实现吧,今天我们就一起来看看操作细节,具体的如下所示。 在开发Android程序的时候,...2016-10-02
  • 实例讲解避免javascript冲突的方法

    这篇文章主要以实例的方式讲解了避免javascript冲突的方法,具有一定的参考价值,感兴趣的朋友可以参考一下...2016-01-07
  • android.os.BinderProxy cannot be cast to com解决办法

    本文章来给大家介绍关于android.os.BinderProxy cannot be cast to com解决办法,希望此文章对各位有帮助呀。 Android在绑定服务的时候出现java.lang.ClassCastExc...2016-09-20
  • Android 实现钉钉自动打卡功能

    这篇文章主要介绍了Android 实现钉钉自动打卡功能的步骤,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下...2021-03-15
  • Android 开发之布局细节对比:RTL模式

    下面我们来看一篇关于Android 开发之布局细节对比:RTL模式 ,希望这篇文章对各位同学会带来帮助,具体的细节如下介绍。 前言 讲真,好久没写博客了,2016都过了一半了,赶紧...2016-10-02
  • C#实现带进度条的ListView

    这篇文章主要介绍了C#实现带进度条的ListView 的相关资料,需要的朋友可以参考下...2020-06-25
  • Android中使用SDcard进行文件的读取方法

    首先如果要在程序中使用sdcard进行存储,我们必须要在AndroidManifset.xml文件进行下面的权限设置: 在AndroidManifest.xml中加入访问SDCard的权限如下: <!--...2016-09-20
  • Android开发之PhoneGap打包及错误解决办法

    下面来给各位简单的介绍一下关于Android开发之PhoneGap打包及错误解决办法,希望碰到此类问题的同学可进入参考一下哦。 在我安装、配置好PhoneGap项目的所有依赖...2016-09-20
  • 用Intel HAXM给Android模拟器Emulator加速

    Android 模拟器 Emulator 速度真心不给力,, 现在我们来介绍使用 Intel HAXM 技术为 Android 模拟器加速,使模拟器运行度与真机比肩。 周末试玩了一下在Eclipse中使...2016-09-20
  • Android判断当前屏幕是全屏还是非全屏

    在安卓开发时我碰到一个问题就是需要实现全屏,但又需要我们来判断出用户是使用了全屏或非全屏了,下面我分别找了两段代码,大家可参考。 先来看一个android屏幕全屏实...2016-09-20
  • Android开发中布局中的onClick简单完成多控件时的监听的利与弊

    本文章来为各位介绍一篇关于Android开发中布局中的onClick简单完成多控件时的监听的利与弊的例子,希望这个例子能够帮助到各位朋友. 首先在一个控件加上这么一句:and...2016-09-20