ScrollView+Fragment+ListView巢狀ListView,麻麻再也不用擔心我不會寫巢狀
阿新 • • 發佈:2019-01-30
前言
之前寫了一篇文章
android ListView/GridView與ScrollView巢狀的滑動衝突解決
介紹瞭如何解決ScrollView與AdapterView的巢狀,但是沒有給出demo,那是因為那些程式碼比較多,而且是在手頭的專案裡,業務邏輯程式碼比較多,所以第一時間沒有分享給大家。
今天終於有空把這些程式碼全部抽離了出來,並且去掉了業務程式碼和不必要的內容,以便大家能夠更好的學習這個知識。
先貼一個效果圖吧:
之前專案裡我是使用複寫ScrollView的onInterceptTouchEvent方法來接管滑動事件的,而有小夥伴@liu告訴我可以使用
android: focusable="true"
android:focusableInTouchMode="true"
來設定ScrollView接管事件。於是本Demo就使用了這個方法,結果與前一種方法效果上沒有發現區別。
ListView巢狀
前一篇文章只寫了滑動衝突解決的問題,本文也不再多贅述了,而ListView與ListView的巢狀裡面還有其他的一些極易出錯之處。
其中ListView中資料混亂(不是指圖片)的問題應該來說是最重要也是比較煩人的bug
我的第一層ListView 的Adapter:
public class QuestionCLAdapter extends MyBaseAdapter<QuestionCL> {
//將下級Adapter放到上級Adapter中維護起來,防止new多個Adapter導致資料錯亂
private HashMap<Integer,QuestionCLOptionAdapter> ads = new HashMap<Integer, QuestionCLOptionAdapter>();
public QuestionCLAdapter(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
@Override
public void setItems(List<QuestionCL> itemList) {
// TODO Auto-generated method stub
super.setItems(itemList);
for (int i = 0; i < itemList.size(); i++) {
ads.put(i,new QuestionCLOptionAdapter(context,itemList.get(i)));
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder holder = null;
if(convertView == null){
convertView = inflater.inflate(R.layout.item_error_question_detail_c, null);
holder = new ViewHolder();
holder.ansysLayout = (LinearLayout) convertView.findViewById(R.id.layout_question_ansys);
holder.questionTitleNum = (TextView) convertView.findViewById(R.id.tv_error_questioncl_titlenum);
holder.questionclTxt = (TextView) convertView.findViewById(R.id.tv_error_questioncl_text);
holder.questionRightTxt = (TextView) convertView.findViewById(R.id.tv_question_rightTxt);
holder.questionansysTxt = (TextView) convertView.findViewById(R.id.txt_question_ansys);
holder.questionclOptionsLv = (ListView) convertView.findViewById(R.id.lv_error_detail_c_option);
convertView.setTag(holder);
}else{
holder = (ViewHolder)convertView.getTag();
}
final QuestionCL item = itemList.get(position);
//初始化介面與監聽器......
if (item.isSubmit()) {
holder.ansysLayout.setVisibility(View.VISIBLE);
int temp = 65;
for (int i = 0; i < item.getItems().size(); i++) {
if (item.getItems().get(i).getIsTrue() == 1) {
temp += i;
}
}
holder.questionRightTxt.setText((char)temp +"");
Log.i(TAG, (char)temp +"");
if (null != item.getAnalysis()) {
holder.questionansysTxt.setText(Html.fromHtml(item.getAnalysis()));
}
}else {
holder.ansysLayout.setVisibility(View.GONE);
}
holder.questionTitleNum.setText(item.getQustionid());
holder.questionclTxt.setText(Html.fromHtml(item.getQustiontext()));
if (null != ads.get(position)) {
holder.questionclOptionsLv.setAdapter(ads.get(position));
}
return convertView;
}
public class ViewHolder{
LinearLayout ansysLayout;
TextView questionTitleNum;
TextView questionRightTxt;
TextView questionansysTxt;
TextView questionclTxt;
ListView questionclOptionsLv;
}
}
應該來說是一個很經典很常規的自定義Adapter實現吧,繼承的是自定義的虛基類。
可以看到我用一個容器作為類變數去管理其下級Adapter,在每次getView的時候都確保用的是它自己的資料而不是 重新new一個Adapter出來。
虛基類:
public abstract class MyBaseAdapter<T> extends BaseAdapter {
protected Context context;
protected String TAG = null;
protected LayoutInflater inflater;
protected List<T> itemList = new ArrayList<T>();
public MyBaseAdapter(Context context) {
this.context = context;
inflater = LayoutInflater.from(context);
TAG = this.getClass().getName();
}
/**
* 判斷資料是否為空
*
* @return 為空返回true,不為空返回false
*/
@Override
public boolean isEmpty() {
return itemList.isEmpty();
}
/**
* 在原有的資料上新增新資料
*
* @param itemList
*/
public void addItems(List<T> itemList) {
this.itemList.addAll(itemList);
notifyDataSetChanged();
}
/**
* 設定為新的資料,舊資料會被清空
*
* @param itemList
*/
public void setItems(List<T> itemList) {
this.itemList.clear();
this.itemList = itemList;
notifyDataSetChanged();
}
/**
* 清空資料
*/
public void clearItems() {
itemList.clear();
notifyDataSetChanged();
}
@Override
public int getCount() {
return itemList.size();
}
@Override
public Object getItem(int position) {
return itemList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
abstract public View getView(int position, View view, ViewGroup viewGroup);
}
下級Adapter:
public class QuestionCLOptionAdapter extends MyBaseAdapter<QuestionOptionInfo> {
/** 當前被選中的位置 -1表示未作答 **/
private int selected;
/** 答案是否已提交 **/
private boolean isSubmmit;
private QuestionCL parentItem;
/** -1未作答,0答錯,1答對 **/
private int isrightAnswered;
public QuestionCLOptionAdapter(Context context) {
super(context);
selected = -1;
isSubmmit = false;
isrightAnswered = -1;
}
public QuestionCLOptionAdapter(Context context,QuestionCL parentItem) {
super(context);
selected = -1;
isSubmmit = parentItem.isSubmit();
this.parentItem = parentItem;
this.setItems(parentItem.getItems());
isrightAnswered = -1;
}
@Override
public View getView(final int position, View convertView, final ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder holder = null;
if(convertView == null){
convertView = inflater.inflate(R.layout.item_list_error_question_option, null);
holder = new ViewHolder();
holder.rightImg = (ImageView) convertView.findViewById(R.id.img_question_option_true);
holder.wrongImg = (ImageView) convertView.findViewById(R.id.img_question_option_false);
holder.checkBox = (CheckBox) convertView.findViewById(R.id.cb_question_option_select);
holder.layout = (LinearLayout) convertView.findViewById(R.id.layout_question_option);
holder.optionTitle = (TextView) convertView.findViewById(R.id.tv_question_option_title);
holder.optionTxt = (TextView) convertView.findViewById(R.id.tv_question_option_text);
convertView.setTag(holder);
}else{
holder = (ViewHolder)convertView.getTag();
}
final QuestionOptionInfo item = itemList.get(position);
//初始化介面與監聽器......
holder.checkBox.setVisibility(View.VISIBLE);
int num = item.getOptionId()+65;
holder.optionTitle.setText((char)num+".");
holder.optionTxt.setText(Html.fromHtml(item.getOptionText()));
holder.layout.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (isSubmmit) {
return;
}else {
if (position == selected) {
selected = -1;
} else {
selected = position;
isSubmmit = true;
if (null != parentItem && !parentItem.isSubmit() && isSubmmit) {
parentItem.setSubmit(true);
}
QuestionCLOptionAdapter.this.notifyDataSetChanged();
}
}
}
});
if (position == selected) {
holder.checkBox.setChecked(true);
} else {
holder.checkBox.setChecked(false);
}
//判斷是否答對
if (isSubmmit) {
if (position == selected && item.getIsTrue() == 1) {
// 答對了
holder.rightImg.setVisibility(View.VISIBLE);
// Log.i(TAG, "true!!!!!!!!!!!!!!!!");
} else if (position == selected) {
// 答錯了
holder.wrongImg.setVisibility(View.VISIBLE);
// Log.i(TAG, "false!!!!!!!!!!!!!!!");
} else {
holder.rightImg.setVisibility(View.GONE);
holder.wrongImg.setVisibility(View.GONE);
}
}
return convertView;
}
/** -1未作答,0答錯,1答對
* @return the isrightAnswered
*/
public int getIsrightAnswered() {
if (isSubmmit) {
return isrightAnswered;
}else {
return -1;
}
}
/**-1未作答,0答錯,1答對
* @param isrightAnswered the isrightAnswered to set
*/
public void setIsrightAnswered(int isrightAnswered) {
if (isSubmmit) {
this.isrightAnswered = isrightAnswered;
}
}
public class ViewHolder{
CheckBox checkBox;
ImageView rightImg;
ImageView wrongImg;
LinearLayout layout;
TextView optionTitle;
TextView optionTxt;
}
}
兩個Adapter之間 你可以用類變數進行資料互動,可以通過bean的某一個值進行互動判斷。在這裡我都有用到。
Activity
public class MainActivity extends FragmentActivity {
private Bundle bundle;
private ScrollView sv;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sv = (ScrollView) findViewById(R.id.sv_main);
setFragment(new QuestionFragment(),initData());
//顯示在頂端
sv.smoothScrollTo(0,0);
}
private QuestionC initData(){
// 生成測試資料
QuestionC questionC = new QuestionC();
//塞大題題乾等資訊
questionC.setMaintext("從前有座山,山裡有座廟,廟裡有個老和尚在講故事。");
List<QuestionCL> cList = new ArrayList<QuestionCL>();
//2小題
for (int i = 1; i < 3; i++) {
QuestionCL cltemp = new QuestionCL();
//塞小題題乾等資訊
cltemp.setQustionid(i+"");
cltemp.setQustiontext("老和尚"+i+"是誰?");
cltemp.setRightitem("釋永信"+i+i);
List<QuestionOptionInfo> optionList = new ArrayList<QuestionOptionInfo>();
//5選項
for (int j = 1; j < 6; j++) {
QuestionOptionInfo otemp = new QuestionOptionInfo();
//塞選項Id,String等
otemp.setOptionId(j-1);
otemp.setOptionText("釋永信"+i+j);
if (otemp.getOptionText().equals(cltemp.getRightitem())) {
otemp.setIsTrue(1);
}else {
otemp.setIsTrue(0);
}
optionList.add(otemp);
}
cltemp.setItems(optionList);
cList.add(cltemp);
}
questionC.setQuestioncls((ArrayList<QuestionCL>) cList);
return questionC;
}
private void setFragment(Fragment fragment,QuestionC question) {
FragmentManager fragManager = getSupportFragmentManager();
FragmentTransaction transaction = fragManager.beginTransaction();
bundle = new Bundle();
bundle.putSerializable("bean", question);
fragment.setArguments(bundle);
transaction.replace(R.id.layout_fragment, fragment);
if (!MainActivity.this.isFinishing()) {
transaction.commitAllowingStateLoss();
}
}
}
Fragment
public class QuestionFragment extends Fragment{
private Context context;
/** 實體 **/
private QuestionC questionC;
/** 大題幹文字 **/
private TextView questionTxt;
/** 小題ListView **/
private ListView xiaotiLv;
private QuestionCLAdapter ad;
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
context = getActivity();
return inflater.inflate(R.layout.fragment_error_detail_c, container,false);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initWidget();
initData();
}
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
getIntentData();
}
protected void getIntentData() {
//取得Bean等資訊
if (null != getArguments()) {
questionC = (QuestionC) getArguments().get("bean");
};
}
protected void initData() {
ad = new QuestionCLAdapter(context);
if (null == questionC) {
Log.i("questionC", "no data!!!");
} else {
ad.setItems(questionC.getQuestioncls());
xiaotiLv.setAdapter(ad);
questionTxt.setText(Html.fromHtml(questionC.getMaintext()));
}
}
protected void initWidget() {
questionTxt = (TextView) getView().findViewById(R.id.tv_error_question_txt);
xiaotiLv = (ListView) getView().findViewById(R.id.lv_error_detail_c_secondary_question);
}
}
補上activity的佈局
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/sv_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f3f3f3"
android:scrollbars="none"
android:focusable="true"
android:focusableInTouchMode="true"
tools:context="com.ysdemo.nestdemo.MainActivity" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<FrameLayout
android:id="@+id/layout_fragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView >
Demo下載地址
積分不夠的小夥伴可以留下郵箱,我會另外發。