Android自定义ScrollView分段加载大文本数据到TextView
以下内容为原创,转载时请注明链接地址:http://www.cnblogs.com/tiantianbyconan/p/3311658.html
这是我现在碰到的一个问题,如果需要在TextView中加载大文本的时候,比如几M的txt文件时,TextView载入的时候会出现卡死的现象,甚至会出现异常等待退出出现。
解决办法之一就是通过“分段”或“分页”来显示数据,在TextView(嵌入在ScrollView之中实现了TextView的滚动)中滚动到底部的时候,再去加载下一部分的数据,依次类推,这样每次加载的数据相对来说都比较小,不会出现卡顿的现象。
遇到的问题是,如何监听ScrollView滚动的位置(滚动到顶部还是底部?)。
如下,通过自定义ScrollView类(BorderScrollView):
1 package com.wangjie.bigtextloadtest; 2 3 import android.content.Context; 4 import android.graphics.Rect; 5 import android.util.AttributeSet; 6 import android.widget.ScrollView; 7 8 /** 9 * Created with IntelliJ IDEA. 10 * Author: wangjie email:tiantian.china.2@gmail.com 11 * Date: 13-9-6 12 * Time: 下午2:06 13 */ 14 public class BorderScrollView extends ScrollView{ 15 private long millis; 16 // 滚动监听者 17 private OnScrollChangedListener onScrollChangedListener; 18 19 public BorderScrollView(Context context) { 20 super(context); 21 } 22 23 public BorderScrollView(Context context, AttributeSet attrs) { 24 super(context, attrs); 25 } 26 27 public BorderScrollView(Context context, AttributeSet attrs, int defStyle) { 28 super(context, attrs, defStyle); 29 } 30 31 @Override 32 protected void onScrollChanged(int l, int t, int oldl, int oldt) { 33 super.onScrollChanged(l, t, oldl, oldt); 34 35 if(null == onScrollChangedListener){ 36 return; 37 } 38 39 long now = System.currentTimeMillis(); 40 41 // 通知监听者当前滚动的具体信息 42 onScrollChangedListener.onScrollChanged(l, t, oldl, oldt); 43 44 if(now - millis > 1000l){ 45 // 滚动到底部(前提:从不是底部滚动到底部) 46 if((this.getHeight() + oldt) != this.getTotalVerticalScrollRange() 47 && (this.getHeight() + t) == this.getTotalVerticalScrollRange()){ 48 49 onScrollChangedListener.onScrollBottom(); // 通知监听者滚动到底部 50 51 millis = now; 52 } 53 } 54 55 // 滚动到顶部(前提:从不是顶部滚动到顶部) 56 if(oldt != 0 && t == 0){ 57 onScrollChangedListener.onScrollTop(); // 通知监听者滚动到顶部 58 } 59 60 61 } 62 63 public OnScrollChangedListener getOnScrollChangedListener() { 64 return onScrollChangedListener; 65 } 66 67 public void setOnScrollChangedListener(OnScrollChangedListener onScrollChangedListener) { 68 this.onScrollChangedListener = onScrollChangedListener; 69 } 70 71 /** 72 * 获得滚动总长度 73 * @return 74 */ 75 public int getTotalVerticalScrollRange() { 76 return this.computeVerticalScrollRange(); 77 } 78 79 @Override 80 protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) { 81 return 0; // 禁止ScrollView在子控件的布局改变时自动滚动 82 } 83 84 }
滚动监听器接口OnScrollChangedListener:
1 package com.wangjie.bigtextloadtest; 2 3 /** 4 * Created with IntelliJ IDEA. 5 * Author: wangjie email:tiantian.china.2@gmail.com 6 * Date: 13-9-6 7 * Time: 下午2:53 8 */ 9 public interface OnScrollChangedListener { 10 /** 11 * 监听滚动变化 12 * @param l 13 * @param t 14 * @param oldl 15 * @param oldt 16 */ 17 public void onScrollChanged(int l, int t, int oldl, int oldt); 18 19 /** 20 * 监听滚动到顶部 21 */ 22 public void onScrollTop(); 23 24 /** 25 * 监听滚动到底部 26 */ 27 public void onScrollBottom(); 28 29 }
滚动监听器的空实现OnScrollChangedListenerSimple(简洁真正用时候的代码):
1 package com.wangjie.bigtextloadtest; 2 3 /** 4 * Created with IntelliJ IDEA. 5 * Author: wangjie email:tiantian.china.2@gmail.com 6 * Date: 13-9-9 7 * Time: 下午2:39 8 */ 9 public class OnScrollChangedListenerSimple implements OnScrollChangedListener{ 10 @Override 11 public void onScrollChanged(int l, int t, int oldl, int oldt) {} 12 13 @Override 14 public void onScrollTop() {} 15 16 @Override 17 public void onScrollBottom() {} 18 }
异步加载分段文本(这里每次加载50行):
1 package com.wangjie.bigtextloadtest; 2 3 import android.content.Context; 4 import android.os.AsyncTask; 5 import android.os.Handler; 6 import android.view.View; 7 8 import java.io.*; 9 10 /** 11 * Created with IntelliJ IDEA. 12 * Author: wangjie email:tiantian.china.2@gmail.com 13 * Date: 13-9-6 14 * Time: 上午11:34 15 */ 16 public class AsyncTextLoadTask extends AsyncTask<Object, String, String> { 17 private Context context; 18 private MainActivity activity; 19 private BufferedReader br; 20 21 public AsyncTextLoadTask(Context context, BufferedReader br) { 22 this.context = context; 23 this.br = br; 24 activity = (MainActivity)context; 25 } 26 27 @Override 28 protected String doInBackground(Object... params) { 29 StringBuilder paragraph = new StringBuilder(); 30 try { 31 32 String line = ""; 33 34 int index = 0; 35 while(index < 50 && (line = br.readLine()) != null){ 36 paragraph.append(line + "\n"); 37 index++; 38 } 39 40 } catch (IOException e) { 41 e.printStackTrace(); 42 } 43 44 return paragraph.toString(); 45 } 46 47 48 @Override 49 protected void onPreExecute() { 50 super.onPreExecute(); 51 } 52 53 @Override 54 protected void onPostExecute(String result) { 55 super.onPostExecute(result); 56 activity.contentTv.setText(result); 57 new Handler().postDelayed(new Runnable() { 58 59 @Override 60 public void run() { 61 activity.contentScroll.scrollTo(0, 0); // 记载完新数据后滚动到顶部 62 } 63 }, 100); 64 activity.isLoading = false; 65 } 66 67 }
真正使用分段加载数据Activity(这里加载一本小说《百年孤独》):
1 package com.wangjie.bigtextloadtest; 2 3 import android.app.Activity; 4 import android.content.Context; 5 import android.os.Bundle; 6 import android.widget.TextView; 7 8 import java.io.BufferedReader; 9 import java.io.IOException; 10 import java.io.InputStream; 11 import java.io.InputStreamReader; 12 13 public class MainActivity extends Activity { 14 public BorderScrollView contentScroll; 15 public TextView contentTv; 16 17 private BufferedReader br; 18 19 private Context context; 20 21 public boolean isLoading; 22 23 24 /** 25 * Called when the activity is first created. 26 */ 27 @Override 28 public void onCreate(Bundle savedInstanceState) { 29 super.onCreate(savedInstanceState); 30 setContentView(R.layout.main); 31 context = this; 32 33 contentTv = (TextView) findViewById(R.id.content); 34 contentScroll = (BorderScrollView) findViewById(R.id.contentScroll); 35 36 // 注册刚写的滚动监听器 37 contentScroll.setOnScrollChangedListener(new OnScrollChangedListenerSimple(){ 38 @Override 39 public void onScrollBottom() { 40 synchronized (MainActivity.class){ 41 if(!isLoading){ 42 isLoading = true; 43 new AsyncTextLoadTask(context, br).execute(); 44 } 45 } 46 } 47 }); 48 49 try{ 50 InputStream is = context.getAssets().open("bngd.txt"); 51 br = new BufferedReader(new InputStreamReader(is)); 52 53 new AsyncTextLoadTask(context, br).execute(); 54 55 }catch(Exception ex){ 56 ex.printStackTrace(); 57 } 58 59 60 } 61 62 63 @Override 64 protected void onDestroy() { 65 super.onDestroy(); 66 if(null != br){ 67 try { 68 br.close(); 69 } catch (IOException e) { 70 e.printStackTrace(); 71 } 72 } 73 } 74 75 }
贴上布局:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:orientation="vertical" 4 android:layout_width="fill_parent" 5 android:layout_height="fill_parent" 6 > 7 <com.wangjie.bigtextloadtest.BorderScrollView 8 android:id="@+id/contentScroll" 9 android:layout_width="fill_parent" 10 android:layout_height="fill_parent" 11 > 12 <LinearLayout 13 android:orientation="vertical" 14 android:layout_width="fill_parent" 15 android:layout_height="fill_parent" 16 > 17 <TextView 18 android:layout_width="fill_parent" 19 android:layout_height="wrap_content" 20 android:text="asdfasdf" 21 /> 22 <TextView 23 android:id="@+id/content" 24 android:layout_width="fill_parent" 25 android:layout_height="wrap_content" 26 android:text="Hello World, MainActivity" 27 /> 28 </LinearLayout> 29 30 </com.wangjie.bigtextloadtest.BorderScrollView> 31 32 33 </LinearLayout>