1. 程式人生 > >ScrollView+Fragment+ListView巢狀ListView,麻麻再也不用擔心我不會寫巢狀

ScrollView+Fragment+ListView巢狀ListView,麻麻再也不用擔心我不會寫巢狀

前言

之前寫了一篇文章
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下載地址
積分不夠的小夥伴可以留下郵箱,我會另外發。