`
if...self
  • 浏览: 1501 次
  • 性别: Icon_minigender_1
  • 来自: 广州
最近访客 更多访客>>
社区版块
存档分类
最新评论

Android 自定义 Adapter

阅读更多

今天在学习 Android Adapter 中遇到一个奇怪的问题,

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

 

   主布局文件

 

<?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"
    android:background="#06a"
    android:padding="10dip" >

    <TextView
        android:id="@+id/tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="myadapter" />

    <ListView
        android:id="@+id/lv_ad"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    </ListView>

    <Button
        android:id="@+id/btn_buy"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="提交" />

</LinearLayout>

   列表项Code

  

<?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" >

    <ImageView
        android:id="@+id/goods_pic"
        android:layout_width="40dip"
        android:layout_height="40dip"
        android:layout_marginLeft="20dip" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="50dp"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/people_name"
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dip"
            android:textSize="20sp" />

        <TextView
            android:id="@+id/goods_price"
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dip"
            android:textSize="18sp" />
    </LinearLayout>

    <CheckBox
        android:id="@+id/cb"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dip" />

    <Button
        android:id="@+id/btn_deal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="查看" />

</LinearLayout>

   要自定义 Adapter 就必须继承 Android基础类之BaseAdapter  

  

   在实现 MyAdpter 之前 参照以前所学的 SimpleAdapter  用法

   

   最为关键的一句 

 

   SimpleAdapter adapter = new SimpleAdapter(this, data, R.layout.list_item, from, to);

 

   SimpleAdapter 源码

public class SimpleAdapter extends BaseAdapter implements Filterable {
    private int[] mTo;
    private String[] mFrom;
    private ViewBinder mViewBinder;

    private List<? extends Map<String, ?>> mData;

    private int mResource;
    private int mDropDownResource;
    private LayoutInflater mInflater;

    private SimpleFilter mFilter;
    private ArrayList<Map<String, ?>> mUnfilteredData;

    /**
     * Constructor
     * 
     * @param context The context where the View associated with this SimpleAdapter is running
     * @param data A List of Maps. Each entry in the List corresponds to one row in the list. The
     *        Maps contain the data for each row, and should include all the entries specified in
     *        "from"
     * @param resource Resource identifier of a view layout that defines the views for this list
     *        item. The layout file should include at least those named views defined in "to"
     * @param from A list of column names that will be added to the Map associated with each
     *        item.
     * @param to The views that should display column in the "from" parameter. These should all be
     *        TextViews. The first N views in this list are given the values of the first N columns
     *        in the from parameter.
     */
    public SimpleAdapter(Context context, List<? extends Map<String, ?>> data,
            int resource, String[] from, int[] to) {
        mData = data;
        mResource = mDropDownResource = resource;
        mFrom = from;
        mTo = to;
        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    
    /**
     * @see android.widget.Adapter#getCount()
     */
    public int getCount() {
        return mData.size();
    }

    /**
     * @see android.widget.Adapter#getItem(int)
     */
    public Object getItem(int position) {
        return mData.get(position);
    }

    /**
     * @see android.widget.Adapter#getItemId(int)
     */
    public long getItemId(int position) {
        return position;
    }

    /**
     * @see android.widget.Adapter#getView(int, View, ViewGroup)
     */
    public View getView(int position, View convertView, ViewGroup parent) {
        return createViewFromResource(position, convertView, parent, mResource);
    }

 

 

   接下来:

    MyAdapter.java

 public class MyAdapter extends BaseAdapter {

	private int[] mTo;
	private String[] mFrom;

	private List<? extends Map<String, ?>> mData;

	private int mResource;
	private LayoutInflater mInflater;


	public MyAdapter(Context context, List<? extends Map<String, ?>> data,
			int resource, String[] from, int[] to) {
		mData = data;
		mResource  = resource;
		mFrom = from;
		mTo = to;//LayoutInflater inflater = LayoutInflater.from(context);  // 其实现原理就是下面这句
		mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
	}
	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return mData.size();
	}

	@Override
	public Object getItem(int position) {
		return mData.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		//
		View view = mInflater.inflate(mResource, null);
		//填充组件
		for (int i = 0; i < mFrom.length; i++) {
			View v = view.findViewById(mTo[i]);
			Object content = mData.get(position).get(mFrom[i]);
			if (v instanceof ImageView) {
				ImageView iv = (ImageView) v;
				iv.setBackgroundResource((Integer)content);
			}
			else if (v instanceof TextView) {
				TextView tv = (TextView)v;
				tv.setText( (String)content); //tv.setText(content.toString());
			}
		}
		}
		return view;
	}




}

   LayoutInflater inflater = LayoutInflater.from(context); 源码 

/**
     * Obtains the LayoutInflater from the given context.
     */
    public static LayoutInflater from(Context context) {
        LayoutInflater LayoutInflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (LayoutInflater == null) {
            throw new AssertionError("LayoutInflater not found.");
        }
        return LayoutInflater;
    }

 TestAdapter

  

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.heart.listviewdemo.R;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;

public class TestAdapter extends Activity {
	private ListView listVIew;

	private static List<Map<String, Object>> data;
	static String[] from;
	static int[] to;
		
	static {
		data = new ArrayList<Map<String, Object>>();
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("pic", R.drawable.p1);
		map.put("name", "移动");
		map.put("price", 10086);
		data.add(map);
		map = new HashMap<String, Object>();
		map.put("pic", R.drawable.p2);
		map.put("name", "联通");
		map.put("price", "10010");
		data.add(map);
		map = new HashMap<String, Object>();
		map.put("pic", R.drawable.p3);
		map.put("name", "电信");
		map.put("price", "0000");
		data.add(map);
		map = new HashMap<String, Object>();
		map.put("pic", R.drawable.p7);
		map.put("name", "Me");
		map.put("price", "1353");
		data.add(map);

		
		from = new String[] { "pic", "name", "price" };
		to = new int[] { R.id.goods_pic, R.id.people_name,
				R.id.goods_price };
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.adapter_ly);
		//
		MyAdapter adapter = new MyAdapter(getApplicationContext(), data, R.layout.myadapter, from, to);
		listVIew = (ListView) findViewById(R.id.lv_ad);
		listVIew.setAdapter(adapter);
	}

}

    

   开始测试程序,结果是

  

 

  仔细查看代码流程并没有错,那好 dedug 一下程序,结果发现

  

 

  content 值为Integer

  所以发生了:java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.CharSequence

  content 值是由下面这句获得的

 

Object content = mData.get(position).get(mFrom[i]);

   

 

   由此推断:在数据方面出错了

  

map.put("price", 10086);
map.put("price", "10010");

   原来一不小心放了 整型 由于定义了 private static List<Map<String, Object>> data;

 

   value 放入什么都没关系的

 

   解决方案

   1.修改 map.put("price","10086");

 

   2.在之前的 SimpleAdapter 的用法有提过,同样的数据 在 SimpleAdapter  测试中不出错

   这是why?

 

   

public class SimpleAdapterDemo extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//
		ListView listView = new ListView(this);
		//

		List<Map<String, Object>> data = new ArrayList<Map<String,Object>>();
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("pic", R.drawable.p1);
		map.put("name", "移动");
		map.put("phone", 10086);
		data.add(map );
		map = new HashMap<String, Object>();
		map.put("pic", R.drawable.p2);
		map.put("name", "联通");
		map.put("phone", 10010);
		data.add(map );
		map = new HashMap<String, Object>();
		map.put("pic", R.drawable.p3);
		map.put("name", "电信");
		map.put("phone", 0000);
		data.add(map );
		map = new HashMap<String, Object>();
		map.put("pic", R.drawable.p7);
		map.put("name", "Me");
		map.put("phone", "13532605287");
		data.add(map );

		String[] from = new String [] {
				"pic",	
				"name",	
				"phone"	
		};
		int[] to = new int [] {
				R.id.people_pic,	
				R.id.people_name,	
				R.id.people_num		
		};
		SimpleAdapter adapter = new SimpleAdapter(this, data, R.layout.list_item, from, to);
		//
		listView.setAdapter(adapter);
		//
		setContentView(listView);
		//
		listView.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				Log.d("TAG", String.valueOf(position));
				Toast.makeText(SimpleAdapterDemo.this, "onItemClick " + position, Toast.LENGTH_LONG).show();
			}
		});
		
		//listView
		listView.setOnItemSelectedListener(new OnItemSelectedListener() {

			@Override
			public void onItemSelected(AdapterView<?> parent, View view,
					int position, long id) {
				Log.d("TAG", String.valueOf(position));
				Toast.makeText(SimpleAdapterDemo.this, "OnItemSelectedListener " + position, Toast.LENGTH_LONG).show();
			}

			@Override
			public void onNothingSelected(AdapterView<?> parent) {
				
			}
		});
	}
}

   程序能运行起来,为什么能运行起来?

 

 

  List<Map<String, Object>> data = new ArrayList<Map<String,Object>>();

   

  如果把它改成这样  List<Map<String, String>> data = new ArrayList<Map<String,String>>(); 问题不就好办了

  但是我们不可能都是String 类型的数据吧 如:map.put("pic", R.drawable.p1);

  SimpleAdapter 如何做到的

  

public SimpleAdapter(Context context, List<? extends Map<String, ?>> data,
            int resource, String[] from, int[] to) {
        mData = data;
        mResource = mDropDownResource = resource;
        mFrom = from;
        mTo = to;
        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    List<? extends Map<String, ?>> data  有 Java 基础的人都知道泛型

 

  

   public View getView(int position, View convertView, ViewGroup parent)  //这个方法很重要,关键是它如何得到一个 View 对象的

 

 

 

    /**
     * @see android.widget.Adapter#getView(int, View, ViewGroup)
     */
    public View getView(int position, View convertView, ViewGroup parent) {
        return createViewFromResource(position, convertView, parent, mResource);
    }

    private View createViewFromResource(int position, View convertView,
            ViewGroup parent, int resource) {
        View v;
        if (convertView == null) {
            v = mInflater.inflate(resource, parent, false);
        } else {
            v = convertView;
        }

        bindView(position, v);

        return v;
    }

   

 

   终于:

  

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[i]);
            if (v != null) {
                final Object data = dataSet.get(from[i]);
                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");
                    }
                }
            }
        }
    }

   

 

  关键是:data.toString();

 

 String text = data == null ? "" : data.toString();
                if (text == null) {
                    text = "";
                }



// 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);

   

   data  不是 Object 类型么? toString(); 不是打印哈希值么?

 

 经过长时间的研究发现  Object 的toString() 方法 并不返回 Object 的哈希值

 

 

public String toString()
{
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

 

 那只好调试程序了,调程序时,竟然发现 jdk 源码无法进入去,只好折腾一番,大家可以参考 这篇文章

 

 

 接下来终于可以调试,jdk 源码了,为了更好的理解运行流程

 

 

public class TestToString {
	public static void main(String[] args) {
		@SuppressWarnings("unused")
		Object obj = 1;
		//String s = (String) obj; //报错
		String s =  obj.toString(); //通过
		new TestToString().test();
	}
	
	public void test(){
		Object obj = 1;
		System.out.println(obj instanceof Integer);
		System.out.println(obj == Integer.valueOf(1));
		System.out.println(obj.getClass());
		obj = obj.toString();
		System.out.println(obj.getClass());
		System.out.println(obj instanceof String);
		System.out.println(obj);
		obj = new Integer(1);
	}
}

 

 在 Object obj = 1; 打个断点

 

 

 发现 obj = Integer  (我想到了 Java 自动装箱与拆箱(Autoboxing and unboxing) )

 

  

  Integer 类重写了 Object 父类的 toString() 方法,这在我们平时写 JavaBean 时也提供一个 toString() 便于测试,但它 的toString() 如何返回 String 的



   

 

 

      原来这句 return new String(buf, true); 返回了对象,上图能看见 buf 值需要重新编译 jdk 部分源码

    

     原因:这是由于Oracle公司打包jdk时,为缩减体积,去除了二进制文件里的一些东西,所以看不到;目前的解决方案是,把jdk的源码导入到eclipse中,重新编译,然后打包,把jdk路径下的rj.jar替换掉。

 

重新编译 jdk 比较繁琐,大家可以参考 JDK源码重新编译——支持eclipse调试JDK源码--转载  或

 解决Debug JDK source 无法查看局部变量的问题方案

 

  修改 Object obj = ”1“;  再调试,只不过 obj=String 了 调用的是 String类的toString(); 返回本身

  

    /**
     * This object (which is already a string!) is itself returned.
     *
     * @return  the string itself.
     */
    public String toString() {
        return this;
    }

 

   总结:

   TextView 的 setText(CharSequence text) 方法 参数是CharSequence 可读可写序列

   然而我们通常这样做 tv.setText((CharSequence) content);  //也许因为 IDE 提示功能,我们习惯性地强转      了。tv.setText( content.toString());  有 java 的自动装箱拆箱,动态编译 支持。使用 toString(); 神马都能返回 String 字符串。虽然在程序上 TextView 显示的绝对是 String 类型的字符串,而不是什么 Integer ,Double 等包装类,数据流转的困难(大家都懂的 Java 是强类型的语言),大家在放数据的 一般情况下页面是放的都是 String 类型,这没什么好担心的,不过 使用 toString() 方法不是很完美么,再说 SimpleAdapter 都是这样做的, 外国人写那个 SimpleAdapter  方法时,的确写得。。。

 

 

 

  • 描述: LogCat 异常信息
  • 大小: 23.3 KB
  • 大小: 5.6 KB
  • 大小: 3.6 KB
  • 大小: 9.5 KB
  • 大小: 22.7 KB
  • 大小: 3.1 KB
分享到:
评论

相关推荐

    Android自定义adapter的listview

    以上就是关于“Android自定义adapter的listview”的主要知识点。自定义Adapter是Android开发中的核心技能之一,掌握好这一技巧,能帮助我们实现各种复杂的界面效果。通过不断实践和优化,我们可以在保证性能的同时,...

    Android自定义Adapter适配器

    总之,自定义Adapter是Android开发中的一个重要技能,它允许开发者灵活地控制数据的显示方式,同时提高了应用的可扩展性和可维护性。通过实践和研究这个自定义Adapter的代码,你将能更好地掌握这一核心技术。

    GridView用自定义Adapter

    当我们需要显示的数据不满足默认Adapter提供的功能时,自定义Adapter就显得尤为重要。本篇将深入探讨如何为GridView创建自定义Adapter,以实现更灵活、个性化的数据展示。 首先,了解Adapter的基本概念。Adapter是...

    Android之自定义Adapter的ListView

    总结起来,自定义Adapter在Android开发中扮演了至关重要的角色,它允许开发者根据需求定制ListView的每一个元素,提供更灵活的界面设计和交互体验。通过创建自定义Adapter并实现相关方法,我们可以实现更复杂的列表...

    Android自定义Adapter的ListView的思路及代码

    总结起来,自定义Adapter是Android开发中实现ListView个性化和功能扩展的重要手段。通过自定义Adapter,开发者可以根据需求创建复杂的数据视图,并处理各种用户交互事件,从而提高应用的用户体验和功能多样性。

    自定义Adapter适应ListView和GridView

    总之,自定义Adapter是Android开发中不可或缺的一部分,它帮助开发者将数据模型与界面元素紧密结合起来,实现高度定制化的用户体验。理解并掌握自定义Adapter的原理和实践,对于提升Android应用的开发能力具有重要...

    ListView中使用自定义Adapter及时更新数据

    本篇将详细讲解如何在ListView中使用自定义Adapter来实现数据的及时更新。 首先,我们需要理解ListView的工作原理。ListView通过Adapter来与数据源进行交互,Adapter是连接数据集和视图的桥梁。它负责从数据集中...

    Android自定义Spinner下拉列表(使用ArrayAdapter和自定义Adapter实现)

    本篇文章将详细介绍如何在Android中自定义Spinner的下拉列表,包括使用ArrayAdapter和自定义Adapter两种方法。 ### 一、使用ArrayAdapter ArrayAdapter是Android提供的基础适配器,它可以方便地将数组数据与...

    android studio 自定义adapter开发闹钟小案例

    在Android应用开发中,我们经常需要处理数据的展示和交互,这时自定义Adapter就显得尤为重要。本案例"android studio 自定义adapter开发闹钟小案例"将带你深入理解如何在Android Studio中利用Adapter来构建一个实用...

    android 制作有图标的文件资源管理器 自定义Adapter

    在Android开发中,创建一个带有图标的文件资源管理器通常涉及到自定义Adapter的实现。Adapter是连接数据源和UI视图的关键组件,它允许我们把数据转化为可显示的视图元素。下面将详细介绍如何在Android中制作这样一个...

    自定义Adapter 实例

    在Android开发中,Adapter是一个非常重要的组件,它起到了数据源与UI展示之间的桥梁作用。当我们需要将数据集合...自定义Adapter是Android开发中的核心技能之一,掌握它能帮助我们更好地构建用户界面和处理数据展示。

    Android ListView 自定义adapter好友列表

    本文将深入探讨如何使用自定义Adapter来创建一个功能丰富的Android ListView好友列表。 首先,我们需要理解ListView的工作原理。ListView依赖于Adapter来提供数据和视图之间的桥梁。Adapter是连接数据源(如...

    Android自定义控件实现导航条IndicatorView

    在Android应用开发中,自定义控件是提升用户体验和界面个性化的重要手段。本文将深入探讨如何实现一个自定义的...不断学习和实践,将帮助你更好地理解和掌握Android自定义控件的精髓,为你的应用带来更丰富的用户体验。

    Android自定义Spinner样式

    以上就是关于Android自定义Spinner样式的详细说明。通过这些方法,你可以打造出与应用主题相符、用户体验良好的Spinner组件。记住,良好的UI设计能够提升用户对应用的满意度,因此花时间在自定义控件样式上是值得的...

    可重用的自定义adapter

    "可重用的自定义adapter"是Android开发中的一个核心概念,允许开发者根据需求定制ListView、GridView等控件的数据展示方式。本项目基于Android Studio,对于使用Eclipse的开发者可能需要进行一些转换。 自定义...

    Android listview例子, 自定义Adapter:含Button控件

    总之,Android中的ListView结合自定义Adapter和Button控件可以创建出丰富的用户界面,用户不仅可以查看列表数据,还能直接在列表项内进行交互。通过理解并实践这些概念,开发者能够更灵活地构建Android应用程序的UI...

    自定义Adapter

    "自定义Adapter"这个主题主要关注如何正确地创建和使用自定义的Adapter,以及解决在使用过程中可能出现的问题,如程序闪退或崩溃。 首先,我们来理解Adapter的基本原理。Adapter是一个接口,它定义了如何将数据集...

    自定义Adapter填充ExpandableListView

    自定义Adapter填充ExpandableListView是Android UI设计中的一个重要技能,它允许开发者根据需求定制列表的显示样式和交互方式。 首先,我们需要了解Adapter的概念。Adapter是连接数据源与UI组件的桥梁,它负责将...

Global site tag (gtag.js) - Google Analytics