解决:Bitmap too large to be uploaded into a texture exception问题

 更新时间:2016年9月20日 19:54  点击:2318
下面我们一起来看一篇关于 解决:Bitmap too large to be uploaded into a texture exception问题解决办法。

最近做项目发现其他手机没有问题,但是出现了一个手机报异常,最难过的是不显示报错信息,弄了很久,才发现了一句话:Bitmap too large to be uploaded into a texture exception,百度一下才知道怎么回事。简单说就是硬件加速的时候,对图片的大小有限制。不同设备可能有不同的最大值。这个问题悲催的地方是,程序貌似没有捕获到这个exception, 结果是程序也不报错,图片也显示不出来。只有看debug log才能发现这个error message.

一个解决的方法是禁止硬件加速,简单粗暴:

 代码如下 复制代码


<application android:hardwareAccelerated="false" ...>

比较好的解决方法是类似google map的实现:将图片分成不同的块,每次加载需要的块。android提供了一个方法:

http://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html

 代码如下 复制代码


public void drawBitmap (Bitmap bitmap, Rect src, RectF dst, Paint paint)
public Bitmap decodeRegion (Rect rect, BitmapFactory.Options options)

采取上述操作后,就可以加载很多图片,同时也可以显示超级大图了。

还有用ImageLoad加载图片,如果出现这个问题我们可以这样处理:

可以对ImageView设置最大高度和最大宽度:

 

 代码如下 复制代码
android:maxHeight="1000dip"
 
android:maxWidth="1000dip"

同时相应的scaleType为FIT_CENTER  FIT_XY   FIT_START FIT_END CENTER_INSIDE

在上面这些scaleType下,当bitmap的高和宽一个大于所设的最大值时,imageloader会自动处理,按比例缩放。当scaleType为 MATRIX  CENTER  CENTER_CROP时  要高宽两者都大于所设的最大值时,imageloder才会处理。

AutoCompleteTextView就是类是百度搜索引擎中的一个下拉框了,就是我们输入内容时自动匹配内容出来了,下面我们一起来看看小编整理的例子。
我们在使用各种搜索引擎的搜索功能时,只需要输入一部分内容,就会显示出后续内容,比如,我们输入一个“1”,下面就会出现”12306″,”123″,”126″等等的提示内容,我们可以快捷选择自己想要搜索的内容进行搜索.如下图:

今天我所写的只是一个简单的,不连接数据库,不访问网络的一个输入提示框,方法如下:

第一步:建立XML文件并照下图编写代码:

图中的 android:completionThreshold=”1″代表我们输入几个字符时才会提示,设置为2的话,我们需要输入两个字符才会提示后面的内容.

第二步:在Activity中编写如下代码:

第三部:运行模拟器:

可以看出,成功的完成了输入提示功能,但是这只是最简单的,后续如果项目需要,还需要做更多复杂的操作,如界面优化等,Android的道路还很远,我和大家一起努力!

上面是一个规定的并没有词库的,下面升级一下。

下面先上我写的代码:

import android.app.Activity;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;

public class Read_historyActivity extends Activity implements
  OnClickListener {
 private AutoCompleteTextView autoTv;

 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  autoTv = (AutoCompleteTextView) findViewById(R.id.autoCompleteTextView1);
  initAutoComplete("history",autoTv);
  Button search = (Button) findViewById(R.id.button1);
  search.setOnClickListener(this);
 }
 @Override
 public void onClick(View v) {
  // 这里可以设定:当搜索成功时,才执行保存操作
  saveHistory("history",autoTv);
 }

 /**
  * 初始化AutoCompleteTextView,最多显示5项提示,使
  * AutoCompleteTextView在一开始获得焦点时自动提示
  * @param field 保存在sharedPreference中的字段名
  * @param auto 要操作的AutoCompleteTextView
  */
 private void initAutoComplete(String field,AutoCompleteTextView auto) {
  SharedPreferences sp = getSharedPreferences("network_url", 0);
  String longhistory = sp.getString("history", "nothing");
  String[]  hisArrays = longhistory.split(",");
  ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
    android.R.layout.simple_dropdown_item_1line, hisArrays);
  //只保留最近的50条的记录
  if(hisArrays.length > 50){
   String[] newArrays = new String[50];
   System.arraycopy(hisArrays, 0, newArrays, 0, 50);
   adapter = new ArrayAdapter<String>(this,
     android.R.layout.simple_dropdown_item_1line, newArrays);
  }
  auto.setAdapter(adapter);
  auto.setDropDownHeight(350);
  auto.setThreshold(1);
  auto.setCompletionHint("最近的5条记录");
  auto.setOnFocusChangeListener(new OnFocusChangeListener() {
   @Override
   public void onFocusChange(View v, boolean hasFocus) {
    AutoCompleteTextView view = (AutoCompleteTextView) v;
    if (hasFocus) {
      view.showDropDown();
    }
   }
  });
 }

 

 /**
  * 把指定AutoCompleteTextView中内容保存到sharedPreference中指定的字符段
  * @param field  保存在sharedPreference中的字段名
  * @param auto  要操作的AutoCompleteTextView
  */
 private void saveHistory(String field,AutoCompleteTextView auto) {
  String text = auto.getText().toString();
  SharedPreferences sp = getSharedPreferences("network_url", 0);
  String longhistory = sp.getString(field, "nothing");
  if (!longhistory.contains(text + ",")) {
   StringBuilder sb = new StringBuilder(longhistory);
   sb.insert(0, text + ",");
   sp.edit().putString("history", sb.toString()).commit();
  }
<SPAN style="BACKGROUND-COLOR: rgb(240,240,240); FONT-FAMILY: monospace; WHITE-SPACE: pre"> }
}</SPAN>

     上面的代码我实现了autocomplettextview的从sharepreference中读取历史记录并显示的功能,当没有任何输入时,提示最新的5项历史记录(这里可以加个条件,当有历史记录时才显示)
              补上布局的代码

<?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:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:text="@string/hello" />
 <LinearLayout android:layout_width="0px"
  android:layout_height="0px" android:focusable="true"
  android:focusableInTouchMode="true"></LinearLayout>
 <AutoCompleteTextView
  android:hint="请输入文字进行搜索" android:layout_height="wrap_content"
  android:layout_width="match_parent"
  android:id="@+id/autoCompleteTextView1">
 </AutoCompleteTextView>
 <Button android:text="搜索" android:id="@+id/button1"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"></Button>
</LinearLayout>

   当之有一个edittext或者auto的时候,进入画面时是默认得到焦点的,要想去除焦点,可以在auto之前加一个o像素的layout,并设置他先得到焦点。


下面出现的是源码内容

 需要注意的是,我这里用到的AutoCompleteTextView的几个方法

1. setAdapter()方法:这里要传递的adapter参数必须是继承ListAdapter和Filterable的,其中arrayAdapter和simpleAdapter都能满足要求,我们常用arrayAdapter,因为他不需要像simpleAdapte那样设置他的显示位置和textview组件。
               要想掌握它,就必须查看他的源码,我们可以看看arrayadapter是如何实现
              凡是继承了Filterable的adapter都必须重写getFilter接口方法

    public Filter getFilter() {
        if (mFilter == null) {
            mFilter = new ArrayFilter();
        }
        return mFilter;
    }          这个filter 就是实现过滤方法的对象,同样,我们可以查看他的源码是如何实现的
          

 /**
     * <p>An array filter constrains the content of the array adapter with
     * a prefix. Each item that does not start with the supplied prefix
     * is removed from the list.</p>
     */
    private class ArrayFilter extends Filter {
        @Override
        protected FilterResults performFiltering(CharSequence prefix) {
            FilterResults results = new FilterResults();

            if (mOriginalValues == null) {
                synchronized (mLock) {
                    mOriginalValues = new ArrayList<T>(mObjects);
                }
            }

            if (prefix == null || prefix.length() == 0) {
                synchronized (mLock) {
                    ArrayList<T> list = new ArrayList<T>(mOriginalValues);
                    results.values = list;
                    results.count = list.size();
                }
            } else {
                String prefixString = prefix.toString().toLowerCase();

                final ArrayList<T> values = mOriginalValues;
                final int count = values.size();

                final ArrayList<T> newValues = new ArrayList<T>(count);

                for (int i = 0; i < count; i++) {
                    final T value = values.get(i);
                    final String valueText = value.toString().toLowerCase();

                    // First match against the whole, non-splitted value
                    if (valueText.startsWith(prefixString)) {
                        newValues.add(value);
                    } else {
                        final String[] words = valueText.split(" ");
                        final int wordCount = words.length;

                        for (int k = 0; k < wordCount; k++) {
                            if (words[k].startsWith(prefixString)) {
                                newValues.add(value);
                                break;
                            }
                        }
                    }
                }

                results.values = newValues;
                results.count = newValues.size();
            }

            return results;
        }
          这是arrayAdapter自定义的一个私有内部类,所谓私有,就意味着你不能通过继承去修改这种过滤方法,同样你也不能直接得到他过滤后结果集results。假如你想使用新的过滤方法,你必须重写getfilter()方法,返回的filter对象是你要新建的filter对象(在里面包含performFiltering()方法重新构造你要的过滤方法)
         
         2.setDropDownHeight方法 ,用来设置提示下拉框的高度,注意,这只是限制了提示下拉框的高度,提示数据集的个数并没有变化
         3.setThreshold方法,设置从输入第几个字符起出现提示
         4.setCompletionHint方法,设置提示框最下面显示的文字
         5.setOnFocusChangeListener方法,里面包含OnFocusChangeListener监听器,设置焦点改变事件
         6.showdropdown方法,让下拉框弹出来
        

        我没有用到的一些方法列举
1.clearListSelection,去除selector样式,只是暂时的去除,当用户再输入时又重新出现
2.dismissDropDown,关闭下拉提示框
3.enoughToFilter,这是一个是否满足过滤条件的方法,sdk建议我们可以重写这个方法
4. getAdapter,得到一个可过滤的列表适配器
5.getDropDownAnchor,得到下拉框的锚计的view的id
6.getDropDownBackground,得到下拉框的背景色
7.setDropDownBackgroundDrawable,设置下拉框的背景色
8.setDropDownBackgroundResource,设置下拉框的背景资源
9.setDropDownVerticalOffset,设置下拉表垂直偏移量,即是list里包含的数据项数目
10.getDropDownVerticalOffset ,得到下拉表垂直偏移量
11..setDropDownHorizontalOffset,设置水平偏移量
12.setDropDownAnimationStyle,设置下拉框的弹出动画
13.getThreshold,得到过滤字符个数
14.setOnItemClickListener,设置下拉框点击事件
15.getListSelection,得到下拉框选中为位置
16.getOnItemClickListener。得到单项点击事件
17.getOnItemSelectedListener得到单项选中事件
18.getAdapter,得到那个设置的适配器

下面我们一起来看一篇Android Design Support Library 02 — CollapsingToolbarLayout&&CardView问题解决办法。

Material Design的第二篇更新啦!这次介绍两个控件CollapsingToolbarLayout&&CardView

1、CollapsingToolbarLayout

5.0之后,折叠效果的App出现了,前段时间google在material design的设计中也推出了这个控件。
Ok,还是先上视频!

 代码如下 复制代码
<android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="256dp"
        android:fitsSystemWindows="true">
        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:contentScrim="#30469b"
            app:expandedTitleMarginStart="48dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">
  
            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                android:src="@mipmap/bg"
                app:layout_collapseMode="parallax"
                app:layout_collapseParallaxMultiplier="0.7"  />
  
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin" />
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

官方提示,使用CollasingToolbarLayout和Toolbar,title用CollapsingToolbar来设置
This setup uses CollapsingToolbarLayout’s app:layout_collapseMode=”pin” to ensure that the Toolbar itself remains pinned to the top of the screen while the view collapses. Even better, when you use CollapsingToolbarLayout and Toolbar together, the title will automatically appear larger when the layout is fully visible, then transition to its default size as it is collapsed. Note that in those cases, you should call setTitle() on the CollapsingToolbarLayout, rather than on the Toolbar itself.

2、CardView

实现了卡片式的并且有阴影效果。

 代码如下 复制代码

<android.support.v7.widget.CardView
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_margin="@dimen/card_margin">
 
       <LinearLayout
           style="@style/Widget.CardContent"
           android:layout_width="match_parent"
           android:layout_height="wrap_content">
 
           <TextView
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:text="界冢伊奈帆(かいづか いなほ)"
               android:textAppearance="@style/TextAppearance.AppCompat.Title" />
 
           <TextView
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:text="主人公。居住在地..." />
 
           <ImageView
               android:id="@+id/iv_ynf"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content" />
 
       </LinearLayout>
</android.support.v7.widget.CardView>

Android开发语言是Java,所以也分主线程、子线程,那么我们如何要主线程来向子线程发送消息,希望子线程来处理,该如何实现呢?

有时候,我们也可能碰到这样子的一种需求:需要主线程来向子线程发送消息,希望子线程来完成什么任务。如果这样子应该怎么做呢?这就是这篇文章将要讨论的内容。

一、HandlerThread类

主线程发送消息给子线程,通常思维逻辑就是:其实很简单,在主线程中实例化一个Handler,然后让他与子线程相关联(只要它与子线程的Looper相关联即可),这样子它处理的消息就是该子线程中的消息队列,而处理的逻辑都是在该子线程中执行的,不会占用主线程的时间。那么我们就来实现一下,看看这样子到底行得通还是行不通。新建项目,修改它的MainActivity的代码,如下即可:

package com.example.handldertest;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.TextView;
public class ThreadHandlerActivity extends Activity{
    
    //创建子线程
    class MyThread extends Thread{
        private Looper looper;//取出该子线程的Looper
        public void run() {
         
            Looper.prepare();//创建该子线程的Looper
            looper = Looper.myLooper();//取出该子线程的Looper
            Looper.loop();//只要调用了该方法才能不断循环取出消息
        }
    }
    
    private TextView tv;
    private MyThread thread;
    
    
    private Handler mHandler;//将mHandler指定轮询的Looper
    
    protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            tv = new TextView(this);
            tv.setText("Handler实验");
            setContentView(tv);
            thread = new MyThread();
            thread.start();//千万别忘记开启这个线程
            //下面是主线程发送消息
            mHandler = new Handler(thread.looper){
                public void handleMessage(android.os.Message msg) {
                    Log.d("当前子线程是----->", Thread.currentThread()+"");
                };
            };
            mHandler.sendEmptyMessage(1);
    }
}


好了,现在运行该程序。有没有得到预期的结果呢?显然没有,因为报错误了,如下:


这是一个空指针错误。这是为什么呢?仔细思考,也不难发现原因。因为当主线程走到第38行时,此时子线程的Looper对象还没有被创建出来,那么此时thread.looper肯定为空了。其实这个时间是很不好控制的,当然了,你可以让主线程休眠2秒后再执行第38行以后的代码。但是如果有很多个子线程都需要主线程类给其分配任务怎么办??那简直要乱套了。所以我们就更好的解决方式。就是android显然也考虑到了这个问题,于是它我们提供了一个HandlerThread类。这个类是专门处理这个问题的。

当主线程中有耗时的操作时,需要在子线程中完成,通常我们就把这个逻辑放在HandlerThread的对象中执行(该对象就是一个子线程),然后在需要开始执行逻辑的地方发送一个Message来通知一下就可以了。下面我们就修改上面的代码,看一看如何使用HandlerThread这个类。修改MainActivity中的代码如下:

package com.example.handldertest;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.widget.TextView;
public class ThreadHandlerActivity extends Activity{
    
    
    private TextView tv;
    private Handler mHandler;//将mHandler指定轮询的Looper
    
    protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            tv = new TextView(this);
            tv.setText("Handler实验");
            setContentView(tv);
        
            //实例化一个特殊的线程HandlerThread,必须给其指定一个名字
            HandlerThread thread = new HandlerThread("handler thread");
            thread.start();//千万不要忘记开启这个线程
            //将mHandler与thread相关联
            mHandler = new Handler(thread.getLooper()){
                public void handleMessage(android.os.Message msg) {
                    Log.d("当前子线程是----->", Thread.currentThread()+"");
                };
            };
            mHandler.sendEmptyMessage(1);//发送消息
    }
}


运行程序,打印的结果如下:


从打印结果来看,当前子线程的名字正是我们所起的那个名字“handler thread"。

你会有疑问,表面上看HandlerThread并没有创建自己的Looper啊?而且既然是一个线程,那么我们肯定也能重写它的run方法吧。在解答你的疑问之前,我们不妨重写它的run方法来看一看会有什么结果。将代码修改如下:

package com.example.handldertest;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.widget.TextView;
public class ThreadHandlerActivity extends Activity{
    
    
    private TextView tv;
    private Handler mHandler;//将mHandler指定轮询的Looper
    
    protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            tv = new TextView(this);
            tv.setText("Handler实验");
            setContentView(tv);
        
            //实例化一个特殊的线程HandlerThread,必须给其指定一个名字
            HandlerThread thread = new HandlerThread("handler thread"){
                @Override
                public void run() {
                    for(int i=0;i<3;i++){
                        Log.d("handler thread run ",i+"");
                    }
                }
            };
//            HandlerThread thread = new HandlerThread("handler thread");
            thread.start();//千万不要忘记开启这个线程
            //将mHandler与thread相关联
            mHandler = new Handler(thread.getLooper()){
                public void handleMessage(android.os.Message msg) {
                    Log.d("当前子线程是----->", Thread.currentThread()+"");
                };
            };
            mHandler.sendEmptyMessage(1);//发送消息
    }
}


红色部分就是我们重写了它的run方法。再云运行程序,打印的结果如下:

for循环的打印结果正常,但是为什么没有打印出”当前子线程“呢。其实这正是我们要解释的地方。还记得上一篇文章中实现与子线程相关联的的Handler,我们是怎么做的吗?没读过的朋友看以点击链接(http://www.cnblogs.com/fuly550871915/p/4889838.html)。其实我们实现Handlei与线程的关联正是写在run方法中的。而对于HandlerThread这样的线程,也是如此。我们翻看这个类的源代码,找到它的run方法,如下:

@Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

    

在源代码的第4行,进行了实例化自己的Looper,如果继续追踪源代码翻看其getLooper方法你会发现,如果一个Handler在与HandlerThread进行绑定时,发现Looper为空,Handler则会一直等待直到Looper被创建出来为止,然后才继续执行后续的代码。所以我们重写了HandlerThread的run方法,肯定就不会去创建Looper对象,那么绑定的Handler就会永远处于等待状态,自然而然就不会打印出”当前子线程“信息了。这也是为什么我们要使用HandlerThread这个特殊的线程,因为使用这个,我们不必关心多线程会混乱,Looper会为空等一系列问题,只要去关心我们要实现的逻辑就行了。

好了,现在做一下简单的总结吧。

 小结:
1. Handler与哪个线程的Looper相关联,那么它的消息处理逻辑就在与之相关的线程中执行,相应的消息的走向也就在相关联的MessageQueue中。(最常见的就是Handler与主线程关联,那么接收Looper回传的消息后的逻辑就会在主线程中执行)
2. 当主线程中需要与子线程进行通信时(比如将耗时操作放在子线程中),建议使用HandlerThread。同时要注意,千万不要去重写它的run方法。

二、一个主线程与子线程互相通信的例子

知识点都说完了。下面我们来写一个具体的例子实践一下吧。新建一个项目,修改它的MainActivity代码,如下:

package com.example.handldertest;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.widget.TextView;
public class ThreadHandlerActivity extends Activity{
    
    
    private TextView tv;
    private Handler mHandler;//与子线程关联的Handler
    private Handler handler;//与主线程关联的Handler
    
    protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            tv = new TextView(this);
            tv.setText("Handler实验");
            setContentView(tv);
        
            //实例化一个特殊的线程HandlerThread,必须给其指定一个名字
            HandlerThread thread = new HandlerThread("handler thread");
            thread.start();//千万不要忘记开启这个线程
            //将mHandler与thread相关联
            mHandler = new Handler(thread.getLooper()){
                public void handleMessage(android.os.Message msg) {
                    Log.d("我是子线程----->", Thread.currentThread()+"");
                    handler.sendEmptyMessage(1);//发送消息给主线程
                };
            };
            
            handler = new Handler(){
                public void handleMessage(android.os.Message msg) {
                    Log.d("我是主线程----->", Thread.currentThread()+"");
                    mHandler.sendEmptyMessage(1);//发送消息给子线程
                };
            };
            mHandler.sendEmptyMessage(1);//发送消息
            handler.sendEmptyMessage(1);//发送消息
    }
}


注释很详细,不解释 了。运行程序,结果如下:


这样子,就会一直循环下去,轮流打印出主线程和子线程。




Android主线程、子线程通信(Thread+handler)

Android是基于Java的,所以也分主线程,子线程!
主线程:实现业务逻辑、UI绘制更新、各子线程串连,类似于将军;
子线程:完成耗时(联网取数据、SD卡数据加载、后台长时间运行)操作,类似于小兵;

一、子线程向主线程发消息(Thread+handler):


1、主线程中定义Handler:

Handler mHandler = new Handler(){  
  
    @Override  
    public void handleMessage(Message msg) {  
        super.handleMessage(msg);  
        switch (msg.what) {  
        case 0:  
            //do something,refresh UI;  
            break;  
        default:  
            break;  
        }  
    }  
      
};

    
2、子线程处理完耗时操作之后发消息给主线程,更新UI:

mHandler.sendEmptyMessage(0);  

这样在子线程与主线程任务分工的条件下完成了消息交互;

二、主线程向子线程发送消息(Thread+handler):

主线程碰到耗时操作要子线程完成,此时发通知给子线程,操作步骤如下:

1、子线程中定义Handler,Handler定义在哪个线程中,就跟那个线程绑定,在线程中绑定Handler需要调用Looper.prepare();方法,主线程中不调用是因为主线程默认帮你调用了;

public class LoopThread implements Runnable {  
  
    public Handler mHandler = null;  
  
    @Override  
    public void run() {  
        Looper.prepare();  
        mHandler = new Handler() {  
            public void handleMessage(Message msg) {  
                String result = NetUtil.getJsonContent("北京");  
                //完成了获取北京天气的操作;  
                Log.i("test", "handler"+result);  
            }  
        };  
        Looper.loop();  
    }  
  
}



其中Looper.prepare();和Looper.loop();维护了一个消息队列,等待消息注入并在子线程中执行;

2、主线程中这样调用:


lThread.mHandler.sendEmptyMessage(0);  

主线程向子线程发消息,让子线程执行指定的操作,在Android中还有一种方法,即:HandlerThread,看下面的例子:

HandlerThread handlerThread = new HandlerThread("jerome");  
handlerThread.start();  
  
/** 
 * 这里要将HandlerThread创建的looper传递给threadHandler,即完成绑定; 
 */  
threadHandler = new Handler(handlerThread.getLooper()) {  
  
    @Override  
    public void handleMessage(Message msg) {  
        super.handleMessage(msg);  
        switch (msg.what) {  
        case 0:  
这儿可以做耗时的操作;  
            Log.i("jerome", "hello,I am sub thread");  
            break;  
        default:  
            break;  
        }  
    }  
};


在Android开发中,有时我们可能会遇到一个ListView中出现不同样式的item,那么我们如何处理不同的样式呢?本文分享两个实例。

还是先看效果图吧


我们再使用listview时,大多时候listview的item大多时候都是一种样式,在很多app中也很常见,但有时候根据需求,可能数据的数量不一样,同个类型的数据显示的位置不同,亦或者有的item需要图片,有的不需要,但是这些又必须在同一个listview中显示,这时我们就需要在listview中显示多种样式的item,首先我们需要考虑的是如何将不同数量的数据装载到ArrayList<~>中呢,先看看下面的listViewItem,。

package com.example.keranbin.myapplication;
import java.util.HashMap;
import java.util.Map;
public class lIstViewItem
{
    //用于区分listview显示的不同item,告诉适配器我这是什么类型,listview适配器根据type决定怎么显示
    public int type;
    //将要显示的数据用HashMap包装好
    public HashMap map ;
    
    public lIstViewItem(int type, HashMap map)
    {
        this.type = type;
        this.map = map;
    }
}


我们通过自定义一个listItem,即可将所有不同类型,不同数量的数据先组装成统一类型listItem即可,然后用arrayList.add(listitem)即可。

/**
     * 这里我们用三种不同的样式进行测试
     **/
    private ArrayList getDatas() {
        viewItemsArraylists = new ArrayList();
        viewItemsArraylists.add(new lIstViewItem(2, getHashMapThreeType("汪星人", "汪星人喜欢吃骨头", "2015-10-18")));
        viewItemsArraylists.add(new lIstViewItem(1, getHashMapSecondType("喵星人", "喵星喜欢吃鱼")));
        viewItemsArraylists.add(new lIstViewItem(0, getHashMapFirstType("猴子")));
        viewItemsArraylists.add(new lIstViewItem(0, getHashMapFirstType("老虎")));
        viewItemsArraylists.add(new lIstViewItem(1, getHashMapSecondType("老母鸡", "老母鸡喜欢吃虫子")));
        return viewItemsArraylists;
    }
    
    //第一种样式,只传输一个数据
    private HashMap getHashMapFirstType(String firstTheme) {
        HashMap hashMap = new HashMap();
        hashMap.put("Theme", firstTheme);
        return hashMap;
    }
    //第二种样式,传输两个数据
    private HashMap getHashMapSecondType(String secondTheme, String secondContent) {
        HashMap hashMap = new HashMap();
        hashMap.put("Theme", secondTheme);
        hashMap.put("Content", secondContent);
        return hashMap;
    }
    //第三种样式,传输三个数据
    private HashMap getHashMapThreeType(String threeTheme, String threeContent, String date) {
        HashMap hashMap = new HashMap();
        hashMap.put("Theme", threeTheme);
        hashMap.put("Content", threeContent);
        hashMap.put("Date", date);
        return hashMap;
    }

    
剩下的就是listViewAdapter的事情啦,和显示一种样式的listViewAdapter不同的一点是我们重写实现父类baseAdapter的两个方法。

//返回当前布局的样式type
    @Override
    public int getItemViewType(int position) {
        return listDatas.get(position).type;
    }
    //返回你有多少个不同的布局
    @Override
    public int getViewTypeCount() {
        return 3;
    }

    
然后在getView中根据需要进行判断决定显示那种样式即可

package com.example.keranbin.myapplication;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
/**
 * Created by keranbin on 2015/10/13.
 */
public class ListViewAdapter extends BaseAdapter {
    private LayoutInflater mLayoutInflater;
    private Context context;
    private ArrayList listDatas;
    public ListViewAdapter(Context context, ArrayList listDatas) {
        this.listDatas = listDatas;
        mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }
    //返回当前布局的样式type
    @Override
    public int getItemViewType(int position) {
        return listDatas.get(position).type;
    }
    //返回你有多少个不同的布局
    @Override
    public int getViewTypeCount() {
        return 3;
    }
    @Override
    public int getCount() {
        return listDatas.size();
    }
    @Override
    public Object getItem(int position) {
        return listDatas.get(position);
    }
    @Override
    public long getItemId(int position) {
        return position;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        lIstViewItem listItem = listDatas.get(position);
        int Type = getItemViewType(position);
        ViewHolderfirstType viewHolderfirstType = null;
        ViewHoldersecondType viewHoldersecondType = null;
        ViewHolderThreeType viewHolderThreeType = null;
        if (convertView == null) {
            switch (Type) {
                case 0:
                    viewHolderfirstType = new ViewHolderfirstType();
                    convertView = mLayoutInflater.inflate(R.layout.activity_first_type_item, null);
                    viewHolderfirstType.tv_first_theme= (TextView) convertView.findViewById(R.id.tv_first_theme);
                    viewHolderfirstType.tv_first_theme.setText(listItem.map.get("Theme").toString());
                    convertView.setTag(viewHolderfirstType);
                    break;
                case 1:
                    viewHoldersecondType = new ViewHoldersecondType();
                    convertView = mLayoutInflater.inflate(R.layout.activity_second_type_item, null);
                    viewHoldersecondType.tv_second_content = (TextView) convertView.findViewById(R.id.tv_second_content);
                    viewHoldersecondType.btn_second_theme = (Button) convertView.findViewById(R.id.btn_second_theme);
                    viewHoldersecondType.tv_second_content.setText(listItem.map.get("Theme").toString());
                    viewHoldersecondType.btn_second_theme.setText(listItem.map.get("Content").toString());
                    convertView.setTag(viewHoldersecondType);
                    break;
                case 2:
                    viewHolderThreeType = new ViewHolderThreeType();
                    convertView = mLayoutInflater.inflate(R.layout.activity_three_type_item, null);
                    viewHolderThreeType.tv_three_content = (TextView) convertView.findViewById(R.id.tv_three_content);
                    viewHolderThreeType.et_three_theme= (EditText) convertView.findViewById(R.id.et_three_theme);
                    viewHolderThreeType.tv_three_time= (TextView) convertView.findViewById(R.id.tv_three_time);
                    viewHolderThreeType.et_three_theme.setText(listItem.map.get("Theme").toString());
                    viewHolderThreeType.tv_three_content.setText(listItem.map.get("Content").toString());
                    viewHolderThreeType.tv_three_time.setText(listItem.map.get("Date").toString());
                    convertView.setTag(viewHolderThreeType);
                    break;
            }
        }else{
            switch (Type){
                case 0:
                    viewHolderfirstType= (ViewHolderfirstType) convertView.getTag();
                    viewHolderfirstType.tv_first_theme.setText(listItem.map.get("Theme").toString());
                    break;
                case 1:
                    viewHoldersecondType= (ViewHoldersecondType) convertView.getTag();
                    viewHoldersecondType.tv_second_content = (TextView) convertView.findViewById(R.id.tv_second_content);
                    viewHoldersecondType.btn_second_theme = (Button) convertView.findViewById(R.id.btn_second_theme);
                    viewHoldersecondType.tv_second_content.setText(listItem.map.get("Theme").toString());
                    viewHoldersecondType.btn_second_theme.setText(listItem.map.get("Content").toString());
                    break;
                case 2:
                    viewHolderThreeType= (ViewHolderThreeType) convertView.getTag();
                    viewHolderThreeType.tv_three_content = (TextView) convertView.findViewById(R.id.tv_three_content);
                    viewHolderThreeType.et_three_theme= (EditText) convertView.findViewById(R.id.et_three_theme);
                    viewHolderThreeType.tv_three_time= (TextView) convertView.findViewById(R.id.tv_three_time);
                    viewHolderThreeType.et_three_theme.setText(listItem.map.get("Theme").toString());
                    viewHolderThreeType.tv_three_content.setText(listItem.map.get("Content").toString());
                    viewHolderThreeType.tv_three_time.setText(listItem.map.get("Date").toString());
                    break;
            }
        }
        return convertView;
    }
    class ViewHolderfirstType {
        TextView tv_first_theme;
    }
    class ViewHoldersecondType {
        TextView tv_second_content;
        Button btn_second_theme;
    }
    class ViewHolderThreeType {
        EditText et_three_theme;
        TextView tv_three_content;
        TextView tv_three_time;
    }
}


第一种样式页面组件主要是一个TextView.

                


第二种样式页面组件主要是一个TextView和一个button.

                        


第三种样式页面组件主要是两个TextView和一个EditText.

                                


activity_main.xml文件非常简单,就一个listView。

    


下面是MainActivity的代码

package com.example.keranbin.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.HashMap;
public class MainActivity extends Activity {
    private ListView listView;                               //页面listview
    private ListViewAdapter listViewAdapter;                 //listview适配器
    private ArrayList viewItemsArraylists;     //Arraylist主要装载的是传给适配器的数据集合
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //初始化页面组件及一些数据
        initView();
        //为listview设置适配器
        ListViewAdapter listViewAdapter = new ListViewAdapter(MainActivity.this, getDatas());
        listView.setAdapter(listViewAdapter);
    }
    //初始化页面组件及一些数据
    private void initView() {
        listView = (ListView) this.findViewById(R.id.listView);
        listViewAdapter = new ListViewAdapter(MainActivity.this, getDatas());
    }
    /**
     * 这里我们用三种不同的样式进行测试
     **/
    private ArrayList getDatas() {
        viewItemsArraylists = new ArrayList();
        viewItemsArraylists.add(new lIstViewItem(2, getHashMapThreeType("汪星人", "汪星人喜欢吃骨头", "2015-10-18")));
        viewItemsArraylists.add(new lIstViewItem(1, getHashMapSecondType("喵星人", "喵星喜欢吃鱼")));
        viewItemsArraylists.add(new lIstViewItem(0, getHashMapFirstType("猴子")));
        viewItemsArraylists.add(new lIstViewItem(0, getHashMapFirstType("老虎")));
        viewItemsArraylists.add(new lIstViewItem(1, getHashMapSecondType("老母鸡", "老母鸡喜欢吃虫子")));
        return viewItemsArraylists;
    }
    //第一种样式,只传输一个数据
    private HashMap getHashMapFirstType(String firstTheme) {
        HashMap hashMap = new HashMap();
        hashMap.put("Theme", firstTheme);
        return hashMap;
    }
    //第二种样式,传输两个数据
    private HashMap getHashMapSecondType(String secondTheme, String secondContent) {
        HashMap hashMap = new HashMap();
        hashMap.put("Theme", secondTheme);
        hashMap.put("Content", secondContent);
        return hashMap;
    }
    //第三种样式,传输三个数据
    private HashMap getHashMapThreeType(String threeTheme, String threeContent, String date) {
        HashMap hashMap = new HashMap();
        hashMap.put("Theme", threeTheme);
        hashMap.put("Content", threeContent);
        hashMap.put("Date", date);
        return hashMap;
    }
}

Android ListView存在多个item样式的处理方法


在项目开发的时候,相信大家可能会遇到一个ListView中出现多个不同的布局,遇到这个问题我的大致思路就是创建多个viewholder,在getViewType的时候设置不同位置的item用不同的viewholder,好了不废话那么多直接上代码:

package com.sunny.youdao;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class MyAdapter extends BaseAdapter {
    private Context mContext;
    private LinearLayout linearLayout = null;
    private LayoutInflater inflater;
    private List list = new ArrayList();
    private TextView tex;
    private final int VIEW_TYPE = 3;
    private final int TYPE_1 = 0;
    private final int TYPE_2 = 1;
    private final int TYPE_3 = 2;
    public MyAdapter(Context context, List list) {
        // TODO Auto-generated constructor stub
        this.mContext = context;
        this.list = list;
        inflater = LayoutInflater.from(mContext);
    }
    @Override
    public int getCount() {
        // TODO 自动生成的方法存根
        return list.size();
    }
    @Override
    public Object getItem(int position) {
        // TODO 自动生成的方法存根
        return list.get(position);
    }
    @Override
    public long getItemId(int position) {
        // TODO 自动生成的方法存根
        return position;
    }
    
    //每个convert view都会调用此方法,获得当前所需要的view样式
    @Override
    public int getItemViewType(int position) {
        // TODO Auto-generated method stub
        int viewtype = position%6;
        if(viewtype == 0)
        return TYPE_1;
        else if(viewtype < 3)
            return TYPE_2;
        else if(viewtype < 6)
            return TYPE_3;
        else
            return TYPE_1;
    }
    
    //返回样式的数量
    @Override
    public int getViewTypeCount() {
        // TODO Auto-generated method stub
        return 3;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        viewHolder1 holder1 = null;
        viewHolder2 holder2 = null;
        viewHolder3 holder3 = null;
        int type = getItemViewType(position);
        // 无convertView,需要new出各个控件
        if (convertView == null) {
            Log.e("convertView = ", "###convertView为空###");
            // 按当前所需的样式,确定new的布局
            switch (type) {
            case TYPE_1:
                convertView = inflater.inflate(R.layout.listitem1, parent,false);
                holder1 = new viewHolder1();
                holder1.textView = (TextView) convertView.findViewById(R.id.textview1);
                holder1.checkBox = (CheckBox) convertView.findViewById(R.id.checkbox);
                Log.e("convertView = ", "布局样式一");
                convertView.setTag(holder1);
                break;
            case TYPE_2:
                convertView = inflater.inflate(R.layout.listitem2, parent,false);
                holder2 = new viewHolder2();
                holder2.textView = (TextView) convertView.findViewById(R.id.textview2);
                Log.e("convertView = ", "布局样式二");
                convertView.setTag(holder2);
                break;
            case TYPE_3:
                convertView = inflater.inflate(R.layout.listitem3, parent,false);
                holder3 = new viewHolder3();
                holder3.textView = (TextView) convertView.findViewById(R.id.textview3);
                holder3.imageView = (ImageView) convertView.findViewById(R.id.imageview);
                Log.e("convertView = ", "布局样式三");
                convertView.setTag(holder3);
                break;
            }
        } else {
            // 有convertView,按样式,取得不用的布局
            switch (type) {
            case TYPE_1:
                holder1 = (viewHolder1) convertView.getTag();
                Log.e("convertView= ", "布局样式一");
                break;
            case TYPE_2:
                holder2 = (viewHolder2) convertView.getTag();
                Log.e("convertView= ", "布局样式二");
                break;
            case TYPE_3:
                holder3 = (viewHolder3) convertView.getTag();
                Log.e("convertView= ", "布局样式三");
                break;
            }
        }
        // 设置资源
        switch (type) {
        case TYPE_1:
            holder1.textView.setText(Integer.toString(position));
            holder1.checkBox.setChecked(true);
            break;
        case TYPE_2:
            holder2.textView.setText(Integer.toString(position));
            break;
        case TYPE_3:
            holder3.textView.setText(Integer.toString(position));
            holder3.imageView.setBackgroundResource(R.drawable.icon);
            break;
        }
        return convertView;
    }
    
    // 各个布局的控件资源
    class viewHolder1 {
        CheckBox checkBox;
        TextView textView;
    }
    class viewHolder2 {
        TextView textView;
    }
    class viewHolder3 {
        ImageView imageView;
        TextView textView;
    }
}


[!--infotagslink--]

相关文章

  • 409错误是什么 http 409错误怎么解决

    409错误是什么?http 409错误怎么解决呢?不少站长在遇到这个错误代码之后都一筹莫展,本次一聚教程网为大家带来了详细的说明,快来看看吧。 409错误是什么: HTTP 40...2017-01-22
  • http 405错误是什么 http 405错误怎么解决

    http 405错误是什么?http 405错误怎么解决?相信很多站长都在找这两个问题的答案,本次小编为大家带来了详细的教程,快来看看吧。 405错误是什么: HTTP 405错误是H...2017-01-22
  • 403错误是什么 403错误怎么解决

    403错误是HTTP状态码的一种,属于“请示错误”,表示服务器拒绝请求。如果在搜索引擎尝试抓取您网站上的有效网页时显示此状态代码,那么,这可能是您的服务器或主机拒绝搜索...2017-01-22
  • 412错误是什么 412错误怎么解决

    412错误是什么?412错误怎么解决?本次一聚教程网将为大家带来详细的介绍,帮助大家全面了解412错误的意思以及解决412错误的方法。 412错误是什么: HTTP 412错误,(Precond...2017-01-22
  • Perl CPAN::Modulelist的解决办法

    今天用CPAN安装Term::ReadLine,报了个这样的错误 Going to read /root/.cpan/sources/modules/03modlist.data.gz Can't locate object method "data" via package "C...2016-11-25
  • 407错误是什么 407错误怎么解决

    407错误是什么?407错误怎么解决?不少站长都遇到过407错误,下面小编将告诉大家如何处理407错误。 407错误是什么: HTTP 407错误是HTTP协议状态码的一种,表示需要代...2017-01-22
  • 406错误是什么 406错误怎么解决

    HTTP 406错误是HTTP协议状态码的一种,表示无法使用请求的内容特性来响应请求的网页。一般是指客户端浏览器不接受所请求页面的 MIME 类型。 而MIME类型是在把输出...2017-01-22
  • 410错误是什么 http 410错误怎么解决

    410错误是HTTP协议状态码的一种,本次一聚教程网将为大家详细介绍HTTP 410错误是什么,以及410错误的解决办法。 410错误是什么: HTTP 410错误是HTTP协议状态码的...2017-01-22
  • HTTP 400错误是什么 HTTP 400错误怎么解决

    每当遇到http错误代码为400,代表客户端发起的请求不符合服务器对请求的某些限制,或者请求本身存在一定的错误,那么HTTP 400错误怎么解决呢?请看下文介绍。 目前400错...2017-01-22
  • PHP传值到不同页面的三种常见方式及php和html之间传值问题

    在项目开发中经常见到不同页面之间传值在web工作中,本篇文章给大家列出了三种常见的方式。接触PHP也有几个月了,本文总结一下这段日子中,在编程过程里常用的3种不同页面传值方法,希望可以给大家参考。有什么意见也希望大...2015-11-24
  • js修改input的type属性问题探讨

    js修改input的type属性有些限制。当input元素还未插入文档流之前,是可以修改它的值的,在ie和ff下都没问题。但如果input已经存在于页面,其type属性在ie下就成了只读属性了,不可以修改。...2013-10-19
  • Mysql常见问题集锦

    1,utf8_bin跟utf8_general_ci的区别 ci是 case insensitive, 即 "大小写不敏感", a 和 A 会在字符判断中会被当做一样的; bin 是二进制, a 和 A 会别区别对待. 例如你运行: SELECT * FROM table WHERE txt = 'a'...2013-10-04
  • Mysql大小写敏感的问题

    一、1 CREATE TABLE NAME(name VARCHAR(10)); 对这个表,缺省情况下,下面两个查询的结果是一样的:复制代码 代码如下: SELECT * FROM TABLE NAME WHERE name='clip'; SELECT * FROM TABLE NAME WH...2015-03-15
  • linux mint 下mysql中文支持问题

    一.mysql默认不支持中文,它的server和db默认是latin1编码.所以我们要将其改变为utf-8编码,因为utf-8包含了地球上大部分语言的二进制编码 1.关闭mysql服务 sudo /etc/init.d/mysql stop 2.修改mysql配置文件 mysql配...2015-10-21
  • Jrebel启动失败解决方案详解

    这篇文章主要介绍了Jrebel启动失败解决方案详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-07-07
  • 小记一次mysql主从配置解决方案

      今天研究了个开源项目,数据库是mysql的,其中的脚本数据需要备份,由于本人的机器时mac pro,而且mac下的数据库连接工具都不怎么好用,就想着如何利用windows下的数据库连接工具使用,并做相关备份,另外windows系统下的sqlyo...2015-10-21
  • php 502 bad gateway的解决方案

    502 bad gateway是php-fpm的问题对于这个问题就是配置参数的问题了,下面我们整理了一些关于php-fpm错误问题的解决办法,具体如下。 今天升级完PHP出现了502 Bad Gat...2016-11-25
  • C#使用队列(Queue)解决简单的并发问题

    这篇文章主要介绍了使用队列(Queue)解决简单的并发问题,讲解的很细致,喜欢的朋友们可以了解一下...2020-06-25
  • windows 10 安装和使用中5个常见问题

    2015年7月29日0点起,Windows 10推送全面开启,Windows7、Windows8.1用户可以免费升级到Windows 10,用户也可以通过系统升级到Windows10,在这过程中,用户会遇到这样那样的问题,下面小编总结了windows 10 安装和使用中5个常见问题,需要的朋友可以参考下...2016-01-27
  • php中session常见问题分析

    PHP的session功能,一直为许多的初学者为难。就连有些老手,有时都被搞得莫名其妙。本文,将这些问题,做一个简单的汇总,以便大家查阅。 1. 错误提示 引用 代...2016-11-25