[转载]Android高手进阶教程(十六)之—Android中万能的BaseAdapter(Spinner,ListView,GridView)的使用! | 第三极 | 移动开发者

mikel阅读(959)

[转载]Android高手进阶教程(十六)之—Android中万能的BaseAdapter(Spinner,ListView,GridView)的使用! | 第三极 | 移动开发者.

大家好!今天给大家讲解一下BaseAdapter(基础适配器)的用法,适配器的作用主要是用来给诸如 (Spinner,ListView,GridView)来填充数据的。而(Spinner,ListView,GridView)都有自己的适配器(记 起来麻烦)。但是BaseAdapter(一招鲜)对他们来说却是通用的,为什么这么说呢,首先我们看一下API文档:

我们看一下BaseAdapter已经实现了ListAdapter和SpinnerAdapter的接口,而GridView的适配器是实现了ListAdapter接口,只不过是二维的。所以说BaseAdapter对他们三者来说是通用的。

下面我来说一下BaseAdapter的主要用法.就是我们定义一个类(如:MyAdapter)而这个类继承BaseAdapter.因为它是 implements了ListAdapter和SpinnerAdapter的接口,所以要实现里面的方法,代码如下(未作任何改动的):

private class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
// TODO Auto-generated method stub
return 0;
}
@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 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
return null;
}
}

为了便于大家理解,老规矩写一个简单的Demo,大家按我的步骤来就OK了.

第一步:新建一个Android工程命名为BaseAdapterDemo.

第二步:修改main.xml代码如下:

<!--?xml version="1.0" encoding="utf-8"?-->

第三步:修该BaseAdapterDemo.java代码如下:

package com.tutor.baseadapter;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
public class BaseAdapterDemo extends Activity {

private Spinner mSpinner;
private ListView mListView;
private GridView mGridView;
private MyAdapter mMyAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setupViews();
}

public void setupViews(){
mMyAdapter = new MyAdapter();
mSpinner = (Spinner)findViewById(R.id.spinner);
mSpinner.setAdapter(mMyAdapter);
mListView = (ListView)findViewById(R.id.listview);
mListView.setAdapter(mMyAdapter);
mGridView = (GridView)findViewById(R.id.gridview);
mGridView.setAdapter(mMyAdapter);
mGridView.setNumColumns(2);

}

//定义自己的适配器,注意getCount和getView方法
private class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
// 这里我就返回10了,也就是一共有10项数据项
return 10;
}
@Override
public Object getItem(int arg0) {
return arg0;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// position就是位置从0开始,convertView是Spinner,ListView中每一项要显示的view
//通常return 的view也就是convertView
//parent就是父窗体了,也就是Spinner,ListView,GridView了.
TextView mTextView = new TextView(getApplicationContext());
mTextView.setText("BaseAdapterDemo");
mTextView.setTextColor(Color.RED);
return mTextView;
}

}
}

第四步:运行程序效果图如下:

效果图一:

效果图二:

等等,平时我在这里就和大家告别了,今天还没完呵呵,因为下面是我们的重点了,我们平常看的应用列表什么的,不是单单的一个TextView就可以 了事 的,所以我们可以在Layout里事先 定义好布局。这里我新建了一个名叫baseadapter_provider.xml文件,代码如下:

<!--?xml version="1.0" encoding="utf-8"?-->


将getView()方法修改如下:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
// position就是位置从0开始,convertView是Spinner,ListView中每一项要显示的view
//通常return 的view也就是convertView
//parent就是父窗体了,也就是Spinner,ListView,GridView了.
// TextView mTextView = new TextView(getApplicationContext());
// mTextView.setText("BaseAdapterDemo");
// mTextView.setTextColor(Color.RED);
// return mTextView;

//LayoutInflater不会的参照我的 Android 高手进阶教程(五)
convertView = LayoutInflater.from(getApplicationContext()).inflate
(R.layout.baseadapter_provider,null);

TextView mTextView = (TextView)convertView.findViewById(R.id.textview);
mTextView.setText("BaseAdapterDemo" + position);
mTextView.setTextColor(Color.RED);
return convertView;
}

再次运行看一下效果图如下:

原文链接:http://blog.csdn.net/Android_Tutor/archive/2010/07/01/5707835.aspx

原文作者:魏祝林

[转载]Android常见控件之SimpleAdapter和List-Android开发源码下载-eoe Android开发者社区_Android开发论坛

mikel阅读(804)

[转载](转)Android常见控件之SimpleAdapter和List-Android开发源码下载-eoe Android开发者社区_Android开发论坛.

一、SimpleAdapter 

     

   这是一个简单的适配器,可以将静态数据映射到XML文件中定义好的视图。你可以指定由Map组成的List(比如ArrayList)类型的数据。在 ArrayList中的每个条目对应List中的一行。Maps包含每一行的数据。你可以指定一个XML布局以指定每一行的视图,根据Map中的数据映射 关键字到指定的视图。绑定数据到视图分两个阶段,首先,如果设置了SimpleAdapter.ViewBinder,那么这个设置的 ViewBinder的setViewValue(Android.view.View, Object, String)将被调用。如果setViewValue的返回值是true,则表示绑定已经完成,将不再调用系统默认的绑定实现。如果返回值为 false,视图将按以下顺序绑定数据:

  • 如果View实现了Checkable(例如CheckBox),期望绑定值是一个布尔类型。
  • TextView.期望绑定值是一个字符串类型,通过调用setViewText(TextView, String)绑定。
  • ImageView,期望绑定值是一个资源id或者一个字符串,通过调用setViewImage(ImageView, int) 或 setViewImage(ImageView, String)绑定数据。

  如果没有一个合适的绑定发生将会抛出IllegalStateException。

  先看一下构造函数:

public SimpleAdapter (Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to)

参数

  context  SimpleAdapter关联的View的运行环境

  data    一个Map组成的List。在列表中的每个条目对应列表中的一行,每一个map中应该包含所有在from参数中指定的键

  resource   一个定义列表项的布局文件的资源ID。布局文件将至少应包含那些在to中定义了的ID

  from         一个将被添加到Map映射上的键名

  to     将绑定数据的视图的ID,跟from参数对应,这些应该全是TextView

  举个例子:


public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ListView lv = (ListView) findViewById(R.id.listView1);
String[] from = { “Text”, “Button” };
int[] to = { R.id.text, R.id.button };
List<Map<String, ?>> list = new ArrayList<Map<String, ?>>();
for (int i = 0; i < 10; i++) {
Map<String, String> m = new HashMap<String, String>();
m.put(“Text”, “Text” + i);
m.put(“Button”, “Button” + i);
list.add(m);
}
SimpleAdapter adapter = new SimpleAdapter(this, list, R.layout.listitem, from, to);
lv.setAdapter(adapter);
}

listitem.xml


<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout xmlns:Android=”http://schemas.Android.com/apk/res/android”
android:layout_width
=”fill_parent”
android:layout_height
=”fill_parent”
android:orientation
=”horizontal” >

<TextView
android:layout_width=”wrap_content”
android:id
=”@+id/text”
android:layout_height
=”wrap_content”
android:layout_weight
=”1″ />

<Button
android:id=”@+id/button”
android:layout_width
=”wrap_content”
android:layout_height
=”wrap_content” />

</LinearLayout>

   ListView中的每一项都包含一个TextView跟一个Button,在SimpleAdapter的构造函数中,我们指定了要绑定的数 据:list, list是一个由Map组成的ArrayList, Map的作用就是连同后面的from, to参数定义数据是如何绑定的,在上面的例子中

String[] from = { “Text”, “Button” };
int[] to = { R.id.text, R.id.button };

   而在for循环中每个map都put进了两个键值对,键名跟from中定义的一一对应,这就表示对于ListView中的每一项,依次寻找在to参数中 定义的资源ID,根据这个资源ID在to参数数组中的位置,找到from参数中对应位置的值,以这个值为键,在list中的相应项(一个Map)中以这个 值为键取出这个键对应的值绑定到这个资源ID对应的视图中.

   在上面的例子中每一个ListItem都包含一个TextView与一个Button,但程序运行起来后会发现,按钮可以点击,而ListItem却无 法点击,而且没有对每一个Button关联响应事件,ListItem无法点击是因为按钮抢占了ListItem的焦点,在listitem.xml而已 文件中对LinearLayout加上一个属性就可解决问题:

    android:descendantFocusability=”blocksDescendants”

   我的们下SimpleAdaper的源码会发现,数据的绑定是能过一个叫bindView的函数实现的


private void bindView(int position, View view) {
final Map dataSet = mData.get(position);
if (dataSet == null) {
return;
}

final ViewBinder binder = mViewBinder;
final String[] from = mFrom;
final int[] to = mTo;
final int count = to.length;

for (int i = 0; i < count; i++) {
final View v = view.findViewById(to);
            if (v != null) {
final Object data = dataSet.get(from);
String text = data == null ? “” : data.toString();
if (text == null) {
text = “”;
}

                boolean bound = false;
if (binder != null) {
bound = binder.setViewValue(v, data, text);
}

if (!bound) {
if (v instanceof Checkable) {
if (data instanceof Boolean) {
((Checkable) v).setChecked((Boolean) data);
} else if (v instanceof TextView) {
// Note: keep the instanceof TextView check at the bottom of these
// ifs since a lot of views are TextViews (e.g. CheckBoxes).
                            setViewText((TextView) v, text);
} else {
throw new IllegalStateException(v.getClass().getName() +
” should be bound to a Boolean, not a ” +
(data == null ? “<unknown type>” : data.getClass()));
}
} else if (v instanceof TextView) {
// Note: keep the instanceof TextView check at the bottom of these
// ifs since a lot of views are TextViews (e.g. CheckBoxes).
                        setViewText((TextView) v, text);
} else if (v instanceof ImageView) {
if (data instanceof Integer) {
setViewImage((ImageView) v, (Integer) data);
} else {
setViewImage((ImageView) v, text);
}
} else {
throw new IllegalStateException(v.getClass().getName() + ” is not a ” +
” view that can be bounds by this SimpleAdapter”);
}
}
}
}
}

   其流程大致是,首先检查SimpleAdapter有没有指定SimpleAdapter.ViewBinder,如果指定了就调用其 setViewValue方法, SimpleAdapter.ViewBinder是一个接口,也只有这一个方法,如果ViewBinder返回true表示我们已经完成了对这个 View的数据绑定,就不再调用系统默认的实现,当然我们也可以设置一个ViewBinder添加一些功能后通过返回false再让系统绑定数据,比如对 按钮添加响应事件,而按钮上的文字由默认实现绑定.通过看bindView的实现就可以明白开始时所说的绑定顺序 了:Checkable,TextView,ImageView.

  我们对ListItem的自定义是通过对SimpleAdapter设置ViewBinder来实现的


        SimpleAdapter.ViewBinder binder = new SimpleAdapter.ViewBinder() {

@Override
public boolean setViewValue(View view, Object data, String textRepresentation) {
if (view instanceof Button) {
final View button = view;
//                    button.setBackgroundDrawable(getResources().getDrawable(R.drawable.ic_launcher));
                    view.setOnClickListener(new OnClickListener() {
LinearLayout listItem = (LinearLayout) button.getParent();
TextView tv = (TextView) listItem.findViewById(R.id.text);

@Override
public void onClick(View v) {
Toast.makeText(AdapterDemoActivity.this, tv.getText(), Toast.LENGTH_SHORT).show();
}
});
return false;
}
return false;
}
};
adapter.setViewBinder(binder);

   系统对每一个view调用binder的setViewValue(此例中是R.id.text和R.id.button,一个TextView与一个 Button),我们首先检测这个view是不是一个Button,如果是的话就关联点击事件,可能通过getParent()函数取得 parentView以找到这个view的兄弟view,比如这个例子中的实现就是点击Button后输出这个Button所在的ListItem中的 TextView上的文字.

  在setViewValue中可以完全自定义我们的实现,比如在Button后加一个TextView,当然可以加任何View,但这样做没任何意义,当你需要这样做时你不需要用SimpleAdater而应该用BaseAdapter:


SimpleAdapter.ViewBinder binder = new SimpleAdapter.ViewBinder() {

@Override
public boolean setViewValue(View view, Object data, String textRepresentation) {
if (view instanceof Button) {
final View button = view;
LinearLayout listItem = (LinearLayout) button.getParent();
TextView textView = new TextView(AdapterDemoActivity.this);
textView.setText(“AA”);
listItem.addView(textView,new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
return false;
}
return false;
}
};
adapter.setViewBinder(binder);

二、ListView


根 据列表的适配器类型,列表分为三种,ArrayAdapter,SimpleAdapter和 SimpleCursorAdapter。其中以ArrayAdapter最为简单,只能展示一行字。 SimpleAdapter有最好的扩充性,可以自定义出各种效果。SimpleCursorAdapter 可以认为是SimpleAdapter对数据库的简单结合,可以方面的把数据库的内容以 列表的形式展示出来。SimpleAdapter继承自AdapterView。我们可以通过 setOnItemClickListener()方法给ListView添加监听器,当用户点击某一个列表 项中执行相应的操作。在监听器中需要复写public abstract void onItemClick (AdapterView<?> parent, View view, int position, long id)方法。

[转载]Adapter应用总结_MQ.H_百度空间

mikel阅读(831)

转载Adapter应用总结_MQ.H_百度空间.

Adapter应用总结

首先来看一下Adapter的体系结构:

 

 

一个Adapter的对象扮演一个 桥梁的角色。这个桥梁连接着一个AdapterView和它所包含的数据。Adapter提供了一个通到数据项的途径。Adapter还负责为在数据集里 的每个数据生项生成一个View。它有一个重要的方法:public abstract View getView (int position,View convertView,ViewGroup parent)。这个方法被setListAdapter(adapter)间接地调用。getView 方法的作用是得到一个View,这个view显示数据项里指定位置的数据,你可以或者手动创建一个view或者从一个XML layout中inflate。当这个view被inflated,它的父view(如GridView,ListView等)将要使用默认的 layout参数,除非你用inflate(int,Android.view.ViewGroup,boolean)方法来指定一个根view并防止附 着在根上。

 

下面分别讲一下它的几个常见的子类:

 

ListAdapter接口:继承于Adapter。ListAdapter是一个ListView和list上的数据之间的桥梁。数据经常来自于一个Cursor,但这不是必须的。ListView能显示任何数据,只要它是被一个ListAdapter包装的。

 

BaseAdapter抽象类:是一个实现了既能在ListView(实现了ListAdapter接口)和Spinner(实现了Spinner接口)里用的Adapter类的一般基类。

 

ArrayAdapter 类:一个管理这样的ListView的ListAdapter:这个ListView被一个数组所支持。这个数组可装任意对象。默认状态下,这个类预期能 这样:提供的资源id与一个单独的TextView相关联。如果你想用一个更复杂的layout,就要用包含了域id的构造函数。这个域id能够与一个在 更大的layout资源里的TextView相关联。它将被在数组里的每个对象的toString()方法所填满。你可以添加通常对象的lists或 arrays。重写你对象的toString()方法来决定list里哪一个写有数据的text将被显示。如果想用一些其它的不同于TextView的 view来显示数组(比如ImageViews),或想有一些除了toString()返回值所填在views里的以外的数据,你就要重写 getView(int,View,ViewGroup)方法来返回你想要的View类型。

 

SimpleAdapter 类:一个使静态数据和在XML中定义的Views对应起来的简单adapter。你可以把list上的数据指定为一个Map范型的ArrayList。 ArrayList里的每一个条目对应于list里的一行。Maps包含着每一行的数据。你先要指定一个XML,这个XML定义了用于显示一行的 view。你还要指定一个对应关系,这个对应关系是从Map的keys对应到指定的views。绑定数据到views发生在两个阶段:

 

如 果一个simpleAdapter.ViewBinder是可用的,那么 SetViewValue(Android.view.View,Object,String)要被调用。如果返回true,那么绑定发生了。如果返回 false,那么如下views将被按顺序地尝试:

 

~实现了Checkable的View(如CheckBox),预期的绑定值是boolen

 

~TextView,预期的绑定值是String,并且SetViewText方法被调用

 

~ImageView,预期的绑定值是一个资源的id或String。并且SetViewImage方法被调用

 

如果没有合适的绑定被发现,一个IllegalStateException被抛出。

 

下面是一个SimpleAdapter的例子:

/*--------------------mail.xml---------------------------*/

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

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"&gt;

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:id="@+id/MyListView"&gt;

/*--------------------simple_list_item_2.xml---------------------------*/

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

android:id="@+id/widget0"

android:layout_width="fill_parent"

android:layout_height="fill_parent"&gt;

android:id="@+id/ItemImage"

android:layout_width="wrap_content"

android:layout_height="wrap_content"&gt;

android:id="@+id/ItemTitle"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="TextView"

android:layout_x="50px"

android:textSize="15pt"&gt;

android:id="@+id/ItemText"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="TextView"

android:layout_x="50px"

android:layout_y="40px"&gt;

android:id="@+id/ItemCheck"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_x="270px"&gt;

/*--------------------TestUIList.java---------------------------*/

public class TestUIList extends Activity {

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

ListView list = (ListView) findViewById(R.id.MyListView);

ArrayList&gt; lstImageItem = new ArrayList&gt;();

String[] str1 = { "row one", "row two", "row three", "row four" };

String[] str2 = { "第一行", "第二行", "第三行", "第四行" };

for (int i = 0; i &lt; str1.length; i++) {

HashMap map = new HashMap();

map.put("ItemImage", R.drawable.icon);

map.put("ItemTitle", str1[i]);

map.put("ItemText", str2[i]);

lstImageItem.add(map);

}

SimpleAdapter saImageItems = new SimpleAdapter(this,

lstImageItem,

R.layout.simple_list_item_2,

new String[] { "ItemImage", "ItemTitle", "ItemText" },

new int[] { R.id.ItemImage, R.id.ItemTitle, R.id.ItemText });

list.setAdapter(saImageItems);

}

}

如果你的ListView的每一行想实现被点击后有响应事件。最省事发的方法是继承一个ListActivity。ListActivity是一个这样的Activity:这个Activity能通过绑定到一个像array或cursor这样的数据源来显示一些items的list,并且当用户选了一个item时,能够暴露事件句柄。

ListActivity拥有一个ListView对象。这个ListView对象能够被绑定到不同的数据源,特别是一个数组或者一个拥有查询结果的Cursor。ListActivity有三种用法,分别是Binding,Screen Layout和Row Layout。下面仅讨论一下Screen Layout:

ListActivity有一个默认的layout。这个 layout是由一个在屏幕中央的、单独的、全屏的list构成。然而,如果你想的话,你可以通过在onCreate()里调用setContentView()方法来设置你自己的view layout的方式制定屏幕layout。要这样做,你自己的view必须包含一个id为“@Android:id/android:list”(或者在代码中有list对象)。

随意地,当你制定这个view是空的时,你能够包含任何类型的view对象来显示。这个“空list”通知者必须有一个id“android:empty”。

注意,最后一定要调用setListAdapter(adapter)方法来把通过Adapter绑定了数据的这个List显示出来。setListAdapter方法间接调用了Adapter的getView方法,其作用是返回你想要的view类型。而且当点击listView里的item时,会根据getView重画这个ListView。例子可参见《Android SDK开发大全》中的“资源管理器“的例子。

想要实现事件监听,就要重写 protected void onListItemClick(ListView l, View v, int position,long id)方法。

想要把在XML中自定义了一行的view逐行显示在ListActivity中自定义的ListView中,并且在每行动态绑定数据的话,一般要自己写一个MyAdapter类,这个Adapter继承BaseAdapter并且其构造函数中至少有一个List参数来实现动态绑定数据。有两个重要的步骤:

1)重写getView方法,其中一重要步骤就是用items.get(position)方法来获得被传入的数据。其中items是一个List,它被赋了传入的List参数的值。position是这个数据在ListView中的行数。Get返回的是E类型.即List中的模板类型。

2)写一个内部类private class ViewHolder。这个内部类只有成员变量,它们就是你想在ListView中的一行里要显示的小View成分。

要想在Adapter中动态传入其它类的数据,就要在构造函数中再增加一个(或更多)List参数。

最后我们给出自己些的MyAdapter配合ListActivity实现监听事件的例子:

/*--------------------MyHome.java-----------------------*/

package com.li.android.myhome;

import java.util.ArrayList;

import java.util.List;

import android.app.AlertDialog;

import android.app.ListActivity;

import android.content.DialogInterface;

import android.os.Bundle;

import android.view.View;

import android.widget.ListView;

public class MyHome extends ListActivity

{

private List items = null;

protected void onCreate(Bundle icicle)

{

super.onCreate(icicle);

setContentView(R.layout.main);

items = new ArrayList();

String[] titles =

{ "默认主题", "主题-A", "主题-B", "中秋佳节", "粉红女郎", "花样年华" };

for (int i = 0; i &lt; titles.length; i++) { String title = titles[i]; items.add(title); } setListAdapter(new MyAdapter(this, items)); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { new AlertDialog.Builder(MyHome.this).setItems( R.array.items_my_dialog, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichcountry) { } }) .show(); } } /*--------------------MyAdapter.java-----------------------*/ package com.li.android.myhome; import java.util.List; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; public class MyAdapter extends BaseAdapter { private LayoutInflater mInflater; private Bitmap icom_theme; private Bitmap icon_selected32; private List items; public MyAdapter(Context context, List it) { mInflater = LayoutInflater.from(context); items = it; icom_theme = BitmapFactory.decodeResource(context.getResources(), R.drawable.theme); icon_selected32 = BitmapFactory.decodeResource(context .getResources(), R.drawable.selected32); } public int getCount() { return items.size(); } public Object getItem(int position) { return items.get(position); } public long getItemId(int position) { return position; } public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; convertView = mInflater.inflate(R.layout.file_row, null); holder = new ViewHolder(); holder.text = (TextView) convertView.findViewById(R.id.text); holder.mTheme = (ImageView) convertView.findViewById(R.id.theme); holder.mSelected32 = (ImageView) convertView .findViewById(R.id.selected32); holder.mTheme.setImageBitmap(icom_theme); String title = items.get(position); holder.text.setText(title); holder.mSelected32.setImageBitmap(icon_selected32); return convertView; } private class ViewHolder { TextView text; ImageView mTheme; ImageView mSelected32; } } /*--------------------main.xml-----------------------*/ xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" &gt;

android:id="@android:id/list"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

&gt;

/*-------------------- file_row.xml-----------------------*/

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

android:id="@+id/widget0"

android:layout_width="fill_parent"

android:layout_height="fill_parent" &gt;

android:layout_width="wrap_content"

android:layout_height="wrap_content"

&gt;

android:id="@+id/text"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:layout_x="50px"

android:textSize="15pt"&gt;

android:id="@+id/selected32"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_x="270px"&gt;

/*--------------------string.xml-----------------------*/

Hello World, 仿熊猫

仿熊猫

应用主题

编辑主题

删除主题

[转载]android 9patch - 梦中梦的日志 - 网易博客

mikel阅读(1267)

[转载]android 9patch – 梦中梦的日志 – 网易博客.

下面是两篇有参考性的文章!

 

 记着:我们来澄清一下这两条不同的线,左边跟顶部的线来定义哪些图像的像素允许在伸缩时被复制。
底部与右边的线用来定义一个相对位置内的图像,视图的内容就放入其中。

 

关键学习ninepatch的用法、一些注意事项、以及如何在项目之中使用

 

 

 

 

 

1:介绍

 

参考 :http://blog.sina.com.cn/s/blog_5033827f0100r4dm.html

 

NinePatch图片以*.9.png结尾,和普通图片的区别是四周多了一个边框(如下图所示):

 

                                 android  9patch - meiyitianabc - 飞得更高!

 

         如上图所示,左边那条黑色线代表图片垂直拉伸的区域,上边的那条黑色线代表水平拉伸区域,右边的黑色线代表内容绘制的垂直区域,下边的黑色线代表内容绘制的水平区域,右边和下边的线是可选的,左边和上边的线不能省略。

 

        采用NinePatch图片做背景,可使背景随着内容的拉伸(缩小)而拉伸(缩小)。那么如何将普通的PNG图片编辑为NinePatch图片呢, Android  SDK/tools目录下提供了编辑器draw9patch.bat,双击即可打开,使用起来很简单了,主要有以下选项:

 

        采用NinePatch图片做背景,可使背景随着内容的拉伸(缩小)而拉伸(缩小)。那么如何将普通的PNG图片编辑为NinePatch图片呢, Android  SDK/tools目录下提供了编辑器draw9patch.bat,双击即可打开,使用起来很简单了,主要有以下选项:

 

  • Zoom: 用来缩放左边编辑区域的大小
  • Patch scale: 用来缩放右边预览区域的大小
  • Show lock: 当鼠标在图片区域的时候显示不可编辑区域
  • Show patches: 在编辑区域显示图片拉伸的区域 (使用粉红色来标示)
  • Show content: 在预览区域显示图片的内容区域(使用浅紫色来标示)
  • Show bad patches: 在拉伸区域周围用红色边框显示可能会对拉伸后的图片产生变形的区域,如果完全消除该内容则图片拉伸后是没有变形的,也就是说,不管如何缩放图片显示都是良 好的。(实际试发现NinePatch编辑器是根据图片的颜色值来区分是否为bad patch的,一边来说只要色差不是太大不用考虑这个设置。)

 

例子:

 

NinePatch是一种很有用的PNG图片格式,它可以在特定区域随文字大小进行缩放。如下:

 

android  9patch - meiyitianabc - 飞得更高!

 

从上图可以看到,背景图片的中间区域会随着文字的大小进行缩放。背景图片是一张NinePatch图片。 NinePatch图片可以使用Android自带的draw9patch工具来制作,该工具在SDK安装路径的tools目录下。执行该工具,然后点击“File”->“open 9-path”打开一张用于制作NinePatch图片的原来图片。在画布的上方和左方的边上画线指定缩放区域,
勾选“Show patches”可显示画定的区域,绿色
为固定大小区域,红色为缩放区域,文字会摆放在红色
区域。制作完后,点击“File”? “save 9-path”保存
图片,draw9patch工具会自动为图片加上*.9.png后缀。
把制作好的图片拷贝进项目的res/drawable目录,然后
编写代码。如下:
android  9patch - meiyitianabc - 飞得更高!

 

 

 

 

 

 

 

 

 

2:使用

 

 

 

传 统UI开发中,如果背景的大小不一样,一般需要为每种大小都 制作一张图片,这在button中尤为明显。当然我们也可以一小块一小块水平重复的画,也可 以垂直的话。在android中专门有一种叫nine patch图片(以 9.png结尾)来解决背景大小不一样时,只用一张背景图片。

用自带的tools\draw9patch.bat 打开一张png图片,我们可以在png图片最外面的空格画一个像素宽的黑线。左边是编辑区,在左边的图中,左边黑线的高度决定了垂直拉升时的扩展区域,也 即当垂直拉伸时,只有这个区域的图片会被拉伸。同理图片上边的黑线长度决定了水平拉升时的扩展区域。右边区域是拉升的效果图,从上之下分别为垂直拉伸,水 平拉伸,以及两个方向的拉升。该工具提供了所见即所得的nine patch png编辑方式 。

android nine patch draw

把编辑后的png保存为 9.png到工程目录的res/drawable目录下,如果你的9.png中没有黑线,那么eclipse是会报错的。

效 果图如下。从中我们也可以理解为什么叫 nine patch,相当于把一张png图分成了9个部分,分别为4个角,4条边,以及一个中间区域,4个角是不做拉升的,所以还能一直保持圆角的状态,而2条水 平边和垂直边分别只做水平和垂直拉伸,所以不会出现边会被拉粗的情况,只有中间用黑线指定的区域做拉伸。结果是图片不会走样

9.png

 

 

 

 

NinePatch是一種「可延展」的PNG圖檔。NinePatch圖檔的用途是製作「可隨文字大小縮放」的圖片,如圖1。

ninepatch-1.png
圖1: 文字背景可隨著文字大小縮放

NinePatch是很有用的圖片格式,可做為widget的「背景圖」。如圖1的範例,其應用程式的設計如下:

  • 文字部份使用TextView元件
  • 使用TextView的XML attribute來設定文字大小
  • 使用TextView的XML attribute來設定一張背景圖
  • 使用NinePatch圖片做為背景圖,如此一來背景圖就可以隨著文字大小縮放

首先,第一個工作就是「製作NinePatch圖檔」,方式如下。

Step 1. 準備一張原始的PNG圖檔,如圖2。

ninepatch-2.png
圖2: 原始PNG圖檔(arrow.png)

Step 2. 啟動Android提供的draw9patch工具,直接執行Android SDK tools/目錄下的draw9patch執行檔即可,如圖3。

ninepatch-3.png
圖3: Android SDK提供的draw9patch工具(點擊看原圖)

Step 3. 開啟原始PNG圖檔,編輯圖檔,如圖4。

ninepatch-4.png
圖4: 開始編輯圖檔(點擊看原圖)

如何編輯NinePatch圖檔

NinePatch圖檔的製作方式是「繪製二條線」,分別在原始圖檔的上方與左方繪出二條「黑線」,黑線所交集的區域即為「可延展」區域。如下圖的粉紅色區域。

ninepatch-5.png
圖5: 定義延展區

「可延展區」是Android框架用來擺放文字的區域,換句話說,文字(TextView)只會被放置在粉紅色區域,並且擺放的原則是「對準粉紅區域的中心點」,即上下置中、左右也置中。非「可延展區」,即綠色部份,並不會隨著文字的大小縮放。所以:

1. 綠色區域是固定大小區域
2. 粉紅色區域是可延展區域、文字擺放於此

圖中的「二條黑線」是怎麼畫出來的呢?方式如下。

Step 4. 移動「Zoom」調整圖檔比例,讓「斑馬線」的大小能適中,如圖6。

ninepatch-6.png
圖6: 調整比例

Step 7: 畫黑線

斑馬線是用來畫黑線的區域,怎麼畫黑線呢?用滑鼠點斑馬線就可以了。要怎麼刪除黑線上?按住「Shift」再點斑馬線即可。斑馬線很不好點,所以如果不是要特意訓練操作滑鼠的技巧以及考驗眼力,善用「Zoom」功能可以幫助黑線的繪製。

勾選「Show patches」選項,即可顯示粉紅色區域,如圖7。

ninepatch-7.png
圖7: 即時顯示可延展區

在draw9patch的右邊也會有縮放的展示圖。

Step 8: 完成NinePatch圖檔

儲存完成的NinePatch圖檔,draw9patch會自動將圖檔的副檔名儲存為*.9.png。完成NinePatch圖檔後,就可以開始寫程式了。

 

 

 

開始寫程式: HelloNinePatch

 

範例HelloNinePatch的實作方式如下。

 

Step 1. 建立一個新的Android專案,命名為HelloNinePatch。

 

Step 2. 將arrow.9.png托曳(drag)到HelloNinePatch專案裡的「res/drawable」目錄下。如圖1。

 

ninepatch-res-1.png
圖1: 將arrow.9.png放進res/drawable資料夾

 

Step 3. 修改UI(res/layout/main.xml),設計出上一篇教學(#30)裡的圖2畫面。main.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"
    >
<Button
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="small world"
    android:textSize="12sp"
    android:background="@drawable/arrow"
    />
<Button
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="big world"
    android:textSize="24sp"
    android:background="@drawable/arrow"
    />
<Button
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="super world"
    android:textSize="48sp"
    android:background="@drawable/arrow"
    />
</LinearLayout>

 

這 裡的做法是,在UI上擺放Button元件,並設定Button上的文字及大小。透過「android:background」屬性的設定,我們 將Button的背景設定為「@drawable/arrow」,即「drawable資源(drawable/目錄下)裡的arrow圖 檔」,Android框架會去找到arrow.9.png檔案。

 

因為arrow.9.png是一張NinePatch圖檔,因此會隨著Button上的文字大小延展。

 

Step 4: 完成HellNinePatch

 

程式碼不需要做任何修改,直接執行HelloNinePatch專案即可

 

 

 

再补充下!

 

 

 

 

1.什么是“9妹”(9patch)?
它是一个对png图片做处理的一个工具,能够为我们生成一个”*.9.png”的图片;
2.何为”*.9.png”?
所谓”*.9.png”这是Android os里所支持的一种特殊的图片格式,用它可以实现部分拉伸;这种图片是经过”9妹“进行特殊处理过的,如果不处理的话,直接用PNG图就会有失真,拉伸不正常的现象出现。
3.它的用途是?
说到用途,这种特殊格式的png图,我也看了网上的相关文章但都是用一个能自适应的button举例子!(如下图)清一色抄袭.. – -、
(此实例咱们直接无视掉,在后面我会给大家灌输游戏中实例)
这个例子是指当button上的字体大小改变,那么文字底下的png图也会自动适应文字。
这似乎表明做Android 软件应用 使用一些组件的的时候会时常用到;
4.那么实际在游戏中到底如何使用呢?什么情况下去使用呢?
….当然啦,身为做游戏我一定要”9妹“利用在咱们游戏中才行,不然岂不是白研究了、经过思考突然想到了一些情况,并且发现“9妹”确实在游戏开发中占有一定的分量!下面我们来先熟习“9妹”工具,然后再跟大家举例,贴图来说明其用途、毕竟有图有真相 呵呵~
启动9妹:
在你Android SDK 路径下 X:\android sdk\tools ,你会找到一个 【draw9patch.bat】,没错这就是9妹啦、官方名 NinePatch ;

提示导入一张png图片,然后真正进入”9妹”的操作界面(如下图):     (图1)

序 列 ① :在拉伸区域周围用红色边框显示可能会对拉伸后的图片产生变形的区域,如果完全消除该内容则图片拉伸后是没有变形的,也就是 说,               不管如何缩放图片显示都是良 好的。 (实际试 发现NinePatch编辑器是根据图片的颜色值来区分是否为bad patch的,一边来说只               要色差不是太大不用考虑这个设置。)
序列 ② :区域是导入的图片,以及可操作区域。
序 列 ③ :这里 zoom:的长条bar 是对导入的图放大缩小操作,这里的放大缩小只是为了让使用者更方便操作,毕竟是对像素点操作比较费                   眼,下面的 patch scale 是序列 ④区域中的三种形态的拉伸后的一个预览操作,可以看到操作后的图片拉伸后的效果。
序列 ④: 区域这里从上到下,依次为:纵向拉伸的效果预览、横向拉伸的效果预览,以及整体拉伸的效果预览
序列 ⑤: 这里如果你勾选上,那么当你鼠标放在 ② 区域内的时候并且当前位置为不可操作区域就会出现lock的一张图,就是显示不可编辑区域 ;
序列 ⑥: 这里勾选上,那么在④ 区域中你就会看到当前操作的像素点在拉伸预览图中的相对位置和效果。
序列 ⑦: 在编辑区域显示图片拉伸的区域;
如何操作:
鼠标左键选取需要拉伸的像素点;  shift+鼠标左键取消当前像素点。
操作区域:

大家看到导入的png图片默认周围多了一像素点,也就是这一圈一像素点就是咱们的可操作区域。但是因为下方和右方可操作区域属于可选区域,不用理会;主要大家注意Left 和 top 操作区域;
Top操作区域的一排像素点,表示横向拉伸的像素点;
Left操作区的一排像素点,表示纵向拉伸的像素点;
下图是我对图片的操作:

[转载]Android SDK更新 Connection to http://dl-ssl.google.com refused 解决方法 - 上树的小笨猪 - 博客频道 - CSDN.NET

mikel阅读(898)

[转载]Android SDK更新 Connection to http://dl-ssl.google.com refused 解决方法 – 上树的小笨猪 – 博客频道 – CSDN.NET.

问题描述

使用SDK Manager更新时出现问题
Failed to fetch URL https://dl-ssl.google.com/Android/repository/repository-6.xml, reason: Connection to https://dl-ssl.google.com refused
Failed to fetch URL http://dl-ssl.google.com/Android/repository/addons_list-1.xml, reason: Connection to http://dl-ssl.google.com refused
Failed to fetch URL https://dl-ssl.google.com/Android/repository/addons_list-1.xml, reason: hostname in certificate didn’t match: <dl-ssl.google.com> != <www.google.com>
更新ADT时无法解析https://dl-ssl.google.com/android/eclipse

解决办法

由于某些众所周知又无法理解的原因,我们大陆使用Google的服务会出现种种问题,譬如Android开发也会出现阻碍。不过首先要说明的是一般情况下 使用SDK Manager更新或者更新Eclipse的ADT插件是没有问题的,我以前也能正常更新,但是昨天不知道节点抽什么风,压根无法连接服务器,出现了上边 的种种问题,下面说一下如果网络抽风的话应该如何解决问题。

第一种方法一劳永逸,直接配置VPN,但是现在想找个速度快又稳定还免费的VPN实在不易,尤其是更新SDK,以几kb/s的速度一个文件需要400多分钟,所以也就放弃了VPN。

另一种方法是使用http协议而不是https协议,因为https协议进行了加密处理,大陆因为无法审查,直接封死,而http协议则进行过滤处理,如果不访问乱七八糟的东西,更新个SDK还是没问题的。
在SDK Manager下Tools->Options打开了SDK Manager的Settings,选中“Force https://… sources to be fetched using http://…”,强制使用http协议。
而在更新ADT插件的时候则使用网址http://dl-ssl.google.com/android/eclipse,而不是https://dl-ssl.google.com/android/eclipse,这个在官方开发文档里也有介绍。
但是昨天的情况就是使用http协议也无法访问。

再说一个比较麻烦的方法,就是直接打开
https://dl-ssl.google.com/android/repository/addons_list.xml
https://dl-ssl.google.com/android/repository/repository.xml
https://dl-ssl.google.com/android/repository/addon.xml
这几个文件,找到你要下载的文件名,直接用迅雷下载,ADT可以直接在官网下载ADT包进行安装。具体方法自己搜索。

最好的方法还是改hosts文件的方法,更新速度较快。Windows在C:\WINDOWS\system32\drivers\etc目录下,Linux用户打开/etc/hosts文件。
打开文件后添加以下内容。

#Google主页
203.208.46.146 www.google.com
#这行是为了方便打开Android开发官网 现在好像不翻墙也可以打开
74.125.113.121 developer.android.com
#更新的内容从以下地址下载
203.208.46.146 dl.google.com
203.208.46.146 dl-ssl.google.com

[转载]解决“Connection to https://dl-ssl.google.com refused”问题 - 发哥 - ITeye技术网站

mikel阅读(850)

[转载]解决“Connection to https://dl-ssl.google.com refused”问题 – 发哥 – ITeye技术网站.

相信一些人刚开始搞Android的安装开发环境的时候会遇到:Failed to fectch URl https://dl-ssl.google.com/Android/repository/addons_list.xml, reason: Connection to https://dl-ssl.google.com refused

 

这个问题可能是系统问题,需要修改一下host文件:

 

方法如下:

  • 用notepad打开C:\WINDOWS\system32\drivers\etc中的hosts文件
  • 在最后一行添加74.125.237.1 dl-ssl.google.com
  • 成功解决问题。

相信一些人刚开始搞Android的安装开发环境的时候会遇到:Failed to fectch URl https://dl-ssl.google.com/android/repository/addons_list.xml, reason: Connection to https://dl-ssl.google.com refused

 

这个问题可能是系统问题,需要修改一下host文件:

 

方法如下:

  • 用notepad打开C:\WINDOWS\system32\drivers\etc中的hosts文件
  • 在最后一行添加74.125.237.1 dl-ssl.google.com
  • 成功解决问题。

[转载]android之ScrollView里嵌套ListView-Android开发实例教程-eoe Android开发者社区_Android开发论坛

mikel阅读(790)

[转载]【新提醒】[版主原创]android之ScrollView里嵌套ListView-Android开发实例教程-eoe Android开发者社区_Android开发论坛.

hi,大家好,研究完ScrollView嵌套ScrollView之后,本人突然又想研究ScrollView里嵌套ListView了。
如果还不知道ScrollView嵌套ScrollView是怎么实现的可以参考http://www.eoeandroid.com/thread-240709-1-1.html
在上篇文章当中其实我没有怎么把原理讲清楚,只是上了原代码,而其实ScrollView里套ScrollView和ScrollView里套ListView是同一个道理的。

按常理,ScrollView套ListView会存在两个问题:

1.里面的listView高度无法算出来,通常只能显示listview的其中一行
2.listview不能滚动

在解决问题一的时候,我在网上找了一大堆资料,怎么怎么让listview显示完整,终于被我找到一个帖子,能解决这个问题
http://blog.csdn.net/hitlion2008/article/details/6737459 (同时我不小心搜到了我的”ScrollView嵌套ScrollView”贴子满天飞…在这里我拜托大家,转贴注明出处啊亲。。。。。)。
按照这个贴子我试了一下,虽然listview的高度出来了,但是存在两个问题:
a.还是只支持ScrollView滚动,listView不会滚动,因为listView的高度已经达到最大,它不需要滚动。
b.listview的优点就是能够复用listItem,如果把listView撑到最大,等于是没有复用listItem,这跟没用listView一样,省不了ui资源,那我还不如直接用linearlayout呢

要怎么才能支持外面的ScrollView和里面的ListView都能滚动呢?

想过很多办法,最终我还是把ScrollView套ScrollView的实现原理搬过来试试看,结果成功了。。。。

其实实现原理很简单ScrollView有一个方法requestDisallowInterceptTouchEvent(boolean);
这个方法是设置是否交出ontouch权限的,如果让外层的scrollview.requestDisallowInterceptTouchEvent(false);那么外层的onTouch权限会失去,这样里面的listview就能
拿到ontouch权限了,listView也就能滚了。
问题是:权限只有一个,要支持两个view都能滚动。这个就有点难实现了吧..

其实这个一点也不难,当手指触到listview的时候,让外面的scrollview交出权限,当手指松开后,外面的scrollview重新获得权限。这样ok了。

且看代码实现:
重写一个InnerListView extends ListView

InnerListView.java

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
  case MotionEvent.ACTION_DOWN:
setParentScrollAble(false);//当手指触到listview的时候,让父ScrollView交出ontouch权限,也就是让父scrollview 停住不能滚动
LogManager.d(“onInterceptTouchEvent down”);
case MotionEvent.ACTION_MOVE:
LogManager.d(“onInterceptTouchEvent move”);
break;
case MotionEvent.ACTION_UP:
LogManager.d(“onInterceptTouchEvent up”);
   case MotionEvent.ACTION_CANCEL:
LogManager.d(“onInterceptTouchEvent cancel”);
   setParentScrollAble(true);//当手指松开时,让父ScrollView重新拿到onTouch权限
break;
default:
break;
}
return super.onInterceptTouchEvent(ev);
}

/**
* 是否把滚动事件交给父scrollview
*
* @param flag
*/
private void setParentScrollAble(boolean flag) {
parentScrollView.requestDisallowInterceptTouchEvent(!flag);//这里的parentScrollView就是listview外面的那个scrollview
}

在这里我要提出的是,listview能滚动的前提是:当listview本身的高度小于listview里的子view。

如果在xml里让listview的高度为wrap_content,可能会出现问题,如:ListView的高度会变成跟子view 的高度一样。

这里最好是让listView的高度固定,平时我们写listView布局的时候也是给一个固定值。如fill_parent或100dip

我写了一个参数叫maxHeight.设置listview的最大高度。是为了确保ListView的高度固定。

private int maxHeight;
public int getMaxHeight() {
return maxHeight;
}
public void setMaxHeight(int maxHeight) {
this.maxHeight = maxHeight;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
if (maxHeight > -1) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
System.out.println(getChildAt(0));
}

源代码:点击下载

[转载]Android之ScrollView嵌套ListView冲突-Android开发源码下载-eoe Android开发者社区_Android开发论坛

mikel阅读(743)

[转载]【新提醒】Android之ScrollView嵌套ListView冲突-Android开发源码下载-eoe Android开发者社区_Android开发论坛.

效果图:

在ScrollView中嵌套使用ListView,ListView只会显示一行多一点。两者进行嵌套,即会发生冲突。
由于ListView本身都继承于ScrollView,一旦在ScrollView中嵌套ScrollView,
那么里面的ScrollView高度计算就会出现问题。
我们也就无法得到想要的效果。
下面进入正题,我们将讨论ScrollView中嵌套ListView情况。
核心解决方案: 重写ListView或者GridView的OnMesure 方法。对GridView同样适用。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE &gt;&gt; 2,

MeasureSpec.AT_MOST);

super.onMeasure(widthMeasureSpec, expandSpec);

}

ScrollView中嵌套ListView:

package com.android.xiaomolongstudio.example.scrollviewlistview;

import java.util.ArrayList;

import java.util.List;

import android.app.Activity;

import android.os.Bundle;

import android.view.Menu;

import android.widget.ArrayAdapter;

import android.widget.ListView;

/**

*

* @author 小尛龙

*

*/

public class MainActivity extends Activity {

ListView listView;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

listView = (ListView) findViewById(R.id.listView);

listView.setAdapter(new ArrayAdapter(this,

android.R.layout.simple_expandable_list_item_1, getData()));

}

private List getData() {

List data = new ArrayList();

for (int i = 0; i &lt; 30; i++) { data.add("测试" + i); } return data; } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.activity_main, menu); return true; } } 

自定义ListView:

 package com.android.xiaomolongstudio.example.scrollviewlistview; import android.content.Context; import android.util.AttributeSet; import android.widget.ListView; public class MyListView extends ListView { public MyListView(Context context) { super(context); } public MyListView(Context context, AttributeSet attrs) { super(context, attrs); } public MyListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE &gt;&gt; 2,

MeasureSpec.AT_MOST);

super.onMeasure(widthMeasureSpec, expandSpec);

}

}

这样出来的效果是这样的:

没有上面的按钮,一进页面直接显示的是ListView内容,怎么一开始就显示头部。
ScrollView有个属性mScrollView.scrollTo(x, y)可以显示位置。
但是实际却没有达到效果,查了说mScrollView.scrollTo(x, y)首次初始化时无效果。
最后我用了mScrollView.smoothScrollTo(0,0);OK

源码下载:点击下载

[转载]android-Viewpager - 一个人的ye - 博客频道 - CSDN.NET

mikel阅读(829)

[转载]android-Viewpager – 一个人的ye – 博客频道 – CSDN.NET.

ViewPager是google SDk中自带的一个附加包的一个类(在Android-compatibility这个文件夹下),可以用来实现屏幕间的切换。

如可以仿一个workspace的效果。

 

 

Actviity:

import com.viewpagerindicator.CirclePageIndicator;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
public class ViewpagerActivity extends FragmentActivity {
TestFragmentAdapter mAdapter;
ViewPager mPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

//The look of this sample is set via a style in the manifest
setContentView(R.layout.simple_circles);

mAdapter = new TestFragmentAdapter(getSupportFragmentManager());

mPager = (ViewPager)findViewById(R.id.pager);
mPager.setAdapter(mAdapter);

CirclePageIndicator indicator = (CirclePageIndicator)findViewById(R.id.indicator);
indicator.setViewPager(mPager);
}

}

simple_circles.xml:

<!--?xml version="1.0" encoding="utf-8"?-->

TestFragmentAdapter:

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

class TestFragmentAdapter extends FragmentPagerAdapter {
protected static final String[] CONTENT = new String[] { "第一页", "第二页", "第三页", "第四页", };

private int mCount = CONTENT.length;

public TestFragmentAdapter(FragmentManager fm) {
super(fm);
}

@Override
public Fragment getItem(int position) {
return TestFragment.newInstance(CONTENT[position % CONTENT.length]);
}

@Override
public int getCount() {
return mCount;
}

public void setCount(int count) {
if (count &gt; 0 &amp;&amp; count &lt;= 10) {
mCount = count;
notifyDataSetChanged();
}
}
}

TestFragment:

package com.shao.pager;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;

public final class TestFragment extends Fragment {
private static final String KEY_CONTENT = "TestFragment:Content";

public static TestFragment newInstance(String content) {
TestFragment fragment = new TestFragment();

fragment.mContent = content;

return fragment;
}

private String mContent = "???";

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if ((savedInstanceState != null) &amp;&amp; savedInstanceState.containsKey(KEY_CONTENT)) {
mContent = savedInstanceState.getString(KEY_CONTENT);
}

TextView text = new TextView(getActivity());
text.setGravity(Gravity.CENTER);
text.setText(mContent);
text.setTextSize(20 * getResources().getDisplayMetrics().density);
text.setPadding(20, 20, 20, 20);

LinearLayout layout = new LinearLayout(getActivity());
layout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
layout.setGravity(Gravity.CENTER);
layout.addView(text);

return layout;
}

@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(KEY_CONTENT, mContent);
}
}

PageIndicator:

package com.viewpagerindicator;

import android.support.v4.view.ViewPager;

public interface PageIndicator extends ViewPager.OnPageChangeListener {
public void setViewPager(ViewPager view);

public void setViewPager(ViewPager view, int initialPosition);
public void setCurrentItem(int item);

public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener);
}

CliclePageIndicator:

package com.viewpagerindicator;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import com.shao.pager.R;

public class CirclePageIndicator extends View implements PageIndicator {
public static final int HORIZONTAL = 0;
public static final int VERTICAL = 1;

private float mRadius;
private final Paint mPaintStroke;
private final Paint mPaintFill;
private ViewPager mViewPager;
private ViewPager.OnPageChangeListener mListener;
private int mCurrentPage;
private int mSnapPage;
private int mCurrentOffset;
private int mScrollState;
private int mPageSize;
private int mOrientation;
private boolean mCentered;
private boolean mSnap;

public CirclePageIndicator(Context context) {
this(context, null);
}

public CirclePageIndicator(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.circlePageIndicatorStyle);
}

public CirclePageIndicator(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);

//Load defaults from resources
final Resources res = getResources();
final int defaultFillColor = res.getColor(R.color.default_circle_indicator_fill_color);
final int defaultOrientation = res.getInteger(R.integer.default_circle_indicator_orientation);
final int defaultStrokeColor = res.getColor(R.color.default_circle_indicator_stroke_color);
final float defaultStrokeWidth = res.getDimension(R.dimen.default_circle_indicator_stroke_width);
final float defaultRadius = res.getDimension(R.dimen.default_circle_indicator_radius);
final boolean defaultCentered = res.getBoolean(R.bool.default_circle_indicator_centered);
final boolean defaultSnap = res.getBoolean(R.bool.default_circle_indicator_snap);

//Retrieve styles attributes
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CirclePageIndicator, defStyle, R.style.Widget_CirclePageIndicator);

mCentered = a.getBoolean(R.styleable.CirclePageIndicator_centered, defaultCentered);
mOrientation = a.getInt(R.styleable.CirclePageIndicator_orientation, defaultOrientation);
mPaintStroke = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintStroke.setStyle(Style.STROKE);
mPaintStroke.setColor(a.getColor(R.styleable.CirclePageIndicator_strokeColor, defaultStrokeColor));
mPaintStroke.setStrokeWidth(a.getDimension(R.styleable.CirclePageIndicator_strokeWidth, defaultStrokeWidth));
mPaintFill = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaintFill.setStyle(Style.FILL);
mPaintFill.setColor(a.getColor(R.styleable.CirclePageIndicator_fillColor, defaultFillColor));
mRadius = a.getDimension(R.styleable.CirclePageIndicator_radius, defaultRadius);
mSnap = a.getBoolean(R.styleable.CirclePageIndicator_snap, defaultSnap);

a.recycle();
}

public void setCentered(boolean centered) {
mCentered = centered;
invalidate();
}

public boolean isCentered() {
return mCentered;
}

public void setFillColor(int fillColor) {
mPaintFill.setColor(fillColor);
invalidate();
}

public int getFillColor() {
return mPaintFill.getColor();
}

public void setOrientation(int orientation) {
switch (orientation) {
case HORIZONTAL:
case VERTICAL:
mOrientation = orientation;
updatePageSize();
requestLayout();
break;

default:
throw new IllegalArgumentException("Orientation must be either HORIZONTAL or VERTICAL.");
}
}

public int getOrientation() {
return mOrientation;
}

public void setStrokeColor(int strokeColor) {
mPaintStroke.setColor(strokeColor);
invalidate();
}

public int getStrokeColor() {
return mPaintStroke.getColor();
}

public void setStrokeWidth(float strokeWidth) {
mPaintStroke.setStrokeWidth(strokeWidth);
invalidate();
}

public float getStrokeWidth() {
return mPaintStroke.getStrokeWidth();
}

public void setRadius(float radius) {
mRadius = radius;
invalidate();
}

public float getRadius() {
return mRadius;
}

public void setSnap(boolean snap) {
mSnap = snap;
invalidate();
}

public boolean isSnap() {
return mSnap;
}

/*
* (non-Javadoc)
*
* @see android.view.View#onDraw(android.graphics.Canvas)
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

int longSize;
int longPaddingBefore;
int longPaddingAfter;
int shortPaddingBefore;
if (mOrientation == HORIZONTAL) {
longSize = getWidth();
longPaddingBefore = getPaddingLeft();
longPaddingAfter = getPaddingRight();
shortPaddingBefore = getPaddingTop();
} else {
longSize = getHeight();
longPaddingBefore = getPaddingTop();
longPaddingAfter = getPaddingBottom();
shortPaddingBefore = getPaddingLeft();
}

final int count = mViewPager.getAdapter().getCount();
final float threeRadius = mRadius * 3;
final float shortOffset = shortPaddingBefore + mRadius;
float longOffset = longPaddingBefore + mRadius;
if (mCentered) {
longOffset += ((longSize - longPaddingBefore - longPaddingAfter) / 2.0f) - ((count * threeRadius) / 2.0f);
}

float dX;
float dY;

//Draw stroked circles
for (int iLoop = 0; iLoop &lt; count; iLoop++) { float drawLong = longOffset + (iLoop * threeRadius); if (mOrientation == HORIZONTAL) { dX = drawLong; dY = shortOffset; } else { dX = shortOffset; dY = drawLong; } canvas.drawCircle(dX, dY, mRadius, mPaintStroke); } //Draw the filled circle according to the current scroll float cx = (mSnap ? mSnapPage : mCurrentPage) * threeRadius; if (!mSnap &amp;&amp; (mPageSize != 0)) { cx += (mCurrentOffset * 1.0f / mPageSize) * threeRadius; } if (mOrientation == HORIZONTAL) { dX = longOffset + cx; dY = shortOffset; } else { dX = shortOffset; dY = longOffset + cx; } canvas.drawCircle(dX, dY, mRadius, mPaintFill); } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { final int count = mViewPager.getAdapter().getCount(); final int longSize = (mOrientation == HORIZONTAL) ? getWidth() : getHeight(); final float halfLongSize = longSize / 2; final float halfCircleLongSize = (count * 3 * mRadius) / 2; final float pointerValue = (mOrientation == HORIZONTAL) ? event.getX() : event.getY(); if ((mCurrentPage &gt; 0) &amp;&amp; (pointerValue &lt; halfLongSize - halfCircleLongSize)) {
setCurrentItem(mCurrentPage - 1);
return true;
} else if ((mCurrentPage &lt; count - 1) &amp;&amp; (pointerValue &gt; halfLongSize + halfCircleLongSize)) {
setCurrentItem(mCurrentPage + 1);
return true;
}
}

return super.onTouchEvent(event);
}

@Override
public void setViewPager(ViewPager view) {
if (view.getAdapter() == null) {
throw new IllegalStateException("ViewPager does not have adapter instance.");
}
mViewPager = view;
mViewPager.setOnPageChangeListener(this);
updatePageSize();
invalidate();
}

private void updatePageSize() {
if (mViewPager != null) {
mPageSize = (mOrientation == HORIZONTAL) ? mViewPager.getWidth() : mViewPager.getHeight();
}
}

@Override
public void setViewPager(ViewPager view, int initialPosition) {
setViewPager(view);
setCurrentItem(initialPosition);
}

@Override
public void setCurrentItem(int item) {
if (mViewPager == null) {
throw new IllegalStateException("ViewPager has not been bound.");
}
mViewPager.setCurrentItem(item);
mCurrentPage = item;
invalidate();
}

@Override
public void onPageScrollStateChanged(int state) {
mScrollState = state;

if (mListener != null) {
mListener.onPageScrollStateChanged(state);
}
}

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
mCurrentPage = position;
mCurrentOffset = positionOffsetPixels;
updatePageSize();
invalidate();

if (mListener != null) {
mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
}

@Override
public void onPageSelected(int position) {
if (mSnap || mScrollState == ViewPager.SCROLL_STATE_IDLE) {
mCurrentPage = position;
mSnapPage = position;
invalidate();
}

if (mListener != null) {
mListener.onPageSelected(position);
}
}

@Override
public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
mListener = listener;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOrientation == HORIZONTAL) {
setMeasuredDimension(measureLong(widthMeasureSpec), measureShort(heightMeasureSpec));
} else {
setMeasuredDimension(measureShort(widthMeasureSpec), measureLong(heightMeasureSpec));
}
}

/**
* Determines the width of this view
*
* @param measureSpec
* A measureSpec packed into an int
* @return The width of the view, honoring constraints from measureSpec
*/
private int measureLong(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);

if (specMode == MeasureSpec.EXACTLY) {
//We were told how big to be
result = specSize;
} else {
//Calculate the width according the views count
final int count = mViewPager.getAdapter().getCount();
result = (int)(getPaddingLeft() + getPaddingRight()
+ (count * 2 * mRadius) + (count - 1) * mRadius + 1);
//Respect AT_MOST value if that was what is called for by measureSpec
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}

private int measureShort(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);

if (specMode == MeasureSpec.EXACTLY) {
//We were told how big to be
result = specSize;
} else {
//Measure the height
result = (int)(2 * mRadius + getPaddingTop() + getPaddingBottom() + 1);
//Respect AT_MOST value if that was what is called for by measureSpec
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}

@Override
public void onRestoreInstanceState(Parcelable state) {
SavedState savedState = (SavedState)state;
super.onRestoreInstanceState(savedState.getSuperState());
mCurrentPage = savedState.currentPage;
mSnapPage = savedState.currentPage;
requestLayout();
}

@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState savedState = new SavedState(superState);
savedState.currentPage = mCurrentPage;
return savedState;
}

static class SavedState extends BaseSavedState {
int currentPage;

public SavedState(Parcelable superState) {
super(superState);
}

private SavedState(Parcel in) {
super(in);
currentPage = in.readInt();
}

@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(currentPage);
}

public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}

@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}

代码下载http://download.csdn.net/detail/shaojie519/4256481