具有单独行布局的ListView无法正确生成视图

时间:2015-07-13 06:15:55

标签: android listview android-listview baseadapter

您好我创建了一个列表视图,在我夸大了四个布局中,我的数据集是图像,文本,音频和视频。所以我相应地给布局充气。一旦数据来自Api,图像被缓存,音频视频和文本分别存储在文件夹和sqlite中。但是当我第二次打开listview随机显示数据或有时无序时。甚至有时候没有显示整个数据。这是我的适配器代码。

public class ListViewAdapter extends BaseAdapter {

ArrayList<ListModel> myList = new ArrayList<ListModel>();
LayoutInflater inflater;
Context context;
int flag = 0;
private static final int TYPE_ITEM1 = 1;
private static final int TYPE_ITEM2 = 2;
private static final int TYPE_ITEM3 = 3;
private static final int TYPE_ITEM4 = 4;
private String url;
private String vPath;
private MediaPlayer mp;



public ListViewAdapter(Context context, ArrayList<ListModel> myList) {
    this.myList = myList;
    this.context = context;
    inflater = LayoutInflater.from(this.context);

}


int type;

@Override
public int getItemViewType(int position) {
    ListModel listModel = myList.get(position);
    String data = listModel.getType();
  /*  if (data.equals("Text")) {
        return type1;
    } else if (data.equals("Image")) {
        return type2;

    }return 0;*/

    if (data.equals("Text")) {
        type = TYPE_ITEM1;
    } else if (data.equals("Image")) {
        type = TYPE_ITEM2;
    } else if (data.equals("Audio")) {
        type = TYPE_ITEM3;
    }else if(data.contains("Video")){
        type=TYPE_ITEM4;
    }

    return type;

}

@Override
public int getViewTypeCount() {
    return myList.size() + 1;
}


@Override
public int getCount() {
    return myList.size();
}


@Override
public ListModel getItem(int position) {
    // return myList.get(position);

    if (position >= myList.size()) {
        return null;
    }
    return myList.get(position);
}

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


@Override
public View getView(final int position, View v, ViewGroup parent) {
    ViewHolder holder = null;
    TextView textView = null;
    ImageView imageView = null;
    VideoView vPlayer = null;
    Button pause = null;
    Button play = null;

    int type = getItemViewType(position);
    System.out.println("getView " + position + " " + v + " type = " + type);
    if (v == null) {
        holder = new ViewHolder();
        if (type == TYPE_ITEM1) {
            v = inflater.inflate(R.layout.list_text, null);
            textView = (TextView) v.findViewById(R.id.text);
        } else if (type == TYPE_ITEM2) {
            v = inflater.inflate(R.layout.list_image, null);
            imageView = (ImageView) v.findViewById(R.id.imgView);
        } else if (type == TYPE_ITEM3) {
            v = inflater.inflate(R.layout.list_audio, null);
            play = (Button) v.findViewById(R.id.btn_play);
            pause = (Button) v.findViewById(R.id.stop);
        } else if (type == TYPE_ITEM4) {
            v = inflater.inflate(R.layout.list_video, null);
            vPlayer = (VideoView) v.findViewById(R.id.video_player);

        }
        holder.textView = textView;
        holder.videoPlayer = vPlayer;
        holder.imageView = imageView;
        holder.play = play;
        holder.stop = pause;
        v.setTag(holder);
    } else {
        holder = (ViewHolder) v.getTag();
    }
    ListModel model = myList.get(position);
    if (holder.play != null) {
        holder.play.setId(position);

    }if (holder.videoPlayer!=null){
        holder.videoPlayer.setId(position);
    }if (holder.stop!=null){
        holder.stop.setId(position);
    }

    String datatype = model.getType();

    if (datatype.equals("Text")) {
        holder.textView.setText(model.getData());
    } else if (datatype.equals("Image")) {
        UrlImageViewHelper.setUrlDrawable(holder.imageView, model.getData());
    } else if (datatype.equals("Audio")) {
        url = model.getData();
    }else if (datatype.equals("Video")){
        vPath=model.getData();

    }
    if (holder.play != null) {
        holder.play.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int i = view.getId();
                ListModel model = myList.get(i);
                String audioUri = model.getData();
                new PlayMusicFromPath().execute(audioUri);
            }
        });
        if (holder.stop!=null){
        holder.stop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

            }
        });}
        if (holder.videoPlayer!=null) {

            final ViewHolder finalHolder = holder;
            holder.videoPlayer.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    int videoId = view.getId();
                    ListModel model1 = myList.get(videoId);
                    String videoUrl = model1.getData();
                    finalHolder.videoPlayer.setVideoPath(videoUrl);



                }
            });
        }

    }

    return v;
}


public static class ViewHolder {
    public TextView textView;
    public ImageView imageView;
    public Button play, stop;
    public VideoView videoPlayer;


}


public void audioPlayer(String fileName) {
    //set up MediaPlayer
    mp = new MediaPlayer();

    try {
        mp.setDataSource(fileName);
    } catch (IllegalArgumentException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalStateException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    try {
        mp.prepare();
    } catch (IllegalStateException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    mp.start();
}

class PlayMusicFromPath extends AsyncTask<String, String, String> {

    // Show Progress bar before downloading Music
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        // Shows Progress Bar Dialog and then call doInBackground method

    }

    // Download Music File from Internet
    @Override
    protected String doInBackground(String... f_url) {
        audioPlayer(f_url[0]);

        return null;
    }

    // Once Music File is downloaded
    @Override
    protected void onPostExecute(String file_url) {
        System.out.print(file_url);

    }
}

意见如下 list_Video包含一个视频视图 list_audio包含两个按钮播放和暂停。 list_text包含一个textView 和list_image包含一个imageview Logcat

addInArray been called, this = android.widget.ListView{443bacd0 VFED.VC. .F....ID 0,0-540,838 #7f0a006d app:id/listview}call stack =
java.lang.Throwable: addInArray
        at android.view.ViewGroup.addInArray(ViewGroup.java:3786)
        at android.view.ViewGroup.addViewInner(ViewGroup.java:3740)
        at android.view.ViewGroup.addViewInLayout(ViewGroup.java:3687)
        at android.widget.ListView.setupChild(ListView.java:1862)
        at android.widget.ListView.makeAndAddView(ListView.java:1815)
        at android.widget.ListView.fillDown(ListView.java:698)
        at android.widget.ListView.fillFromTop(ListView.java:759)
        at android.widget.ListView.layoutChildren(ListView.java:1631)
        at android.widget.AbsListView.onLayout(AbsListView.java:2150)
        at android.view.View.layout(View.java:15131)
        at android.view.ViewGroup.layout(ViewGroup.java:4862)
        at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1888)
        at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1877)
        at android.widget.LinearLayout.onLayout(LinearLayout.java:1653)
        at android.view.View.layout(View.java:15131)
        at android.view.ViewGroup.layout(ViewGroup.java:4862)
        at android.widget.FrameLayout.layoutChildren(FrameLayout.java:515)
        at android.widget.FrameLayout.onLayout(FrameLayout.java:450)
        at android.view.View.layout(View.java:15131)
        at android.view.ViewGroup.layout(ViewGroup.java:4862)
        at android.support.v7.internal.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:493)
        at android.view.View.layout(View.java:15131)
        at android.view.ViewGroup.layout(ViewGroup.java:4862)
        at android.widget.FrameLayout.layoutChildren(FrameLayout.java:515)
        at android.widget.FrameLayout.onLayout(FrameLayout.java:450)
        at android.view.View.layout(View.java:15131)
        at android.view.ViewGroup.layout(ViewGroup.java:4862)
        at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1888)
        at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1742)
        at android.widget.LinearLayout.onLayout(LinearLayout.java:1651)
        at android.view.View.layout(View.java:15131)
        at android.view.ViewGroup.layout(ViewGroup.java:4862)
        at android.widget.FrameLayout.layoutChildren(FrameLayout.java:515)
        at android.widget.FrameLayout.onLayout(FrameLayout.java:450)
        at android.view.View.layout(View.java:15131)
        at android.view.ViewGroup.layout(ViewGroup.java:4862)
        at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2323)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2029)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1192)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6231)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:788)
        at android.view.Choreographer.doCallbacks(Choreographer.java:591)
        at android.view.Choreographer.doFrame(Choreographer.java:560)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:774)
        at android.os.Handler.handleCallback(Handler.java:808)
        at android.os.Handler.dispatchMessage(Handler.java:103)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:5292)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:824)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:640)
        at dalvik.system.NativeStart.main(Native Method)

我也看到这些线条.. myprojectname while﹕ A105 to many

2 个答案:

答案 0 :(得分:1)

在视图类型计数中,您需要提供不同类型的视图计数而不是列表计数。在您的情况下,它应该是四(文本,图像,音频和视频)

@Override
public int getViewTypeCount() {
    return 4;
}

答案 1 :(得分:0)

这是列表视图的常见问题。 首先要解释发生了什么。 当您的列表视图滚动时,它将尝试重用视图,而不是创建一个全新的视图。 如果位置1处的项目是图像,并且列表重新使用位置5处的视图,但位置5是视频数据,因此如果您不隐藏图像视图,则图像视图和视频视图都将显示。它似乎是随机的,但编程中的任何东西都是随机的,除非你按照这种方式编程。

这是我的建议。要使其与您所需的一致,您需要将视图持有者中的所有其他视图显式设置为visibility="gone",但该项目位置所需的视图除外。

holder.textView.setVisibility(View.GONE);
holder.imageView.setVisibility(View.GONE);
holder.vPlayer.setVisibility(View.GONE);
holder.play.setVisibility(View.GONE);
holder.pause.setVisibility(View.GONE);

if (datatype.equals("Text")) {
    holder.textView.setText(model.getData());
    holder.textView.setVisibility(View.VISIBLE);
} else if (datatype.equals("Image")) {
    UrlImageViewHelper.setUrlDrawable(holder.imageView, model.getData());
    holder.imageView.setVisibility(View.VISIBLE);
} else if (datatype.equals("Audio")) {
    url = model.getData();
    holder.play.setVisibility(View.VISIBLE);
    holder.pause.setVisibility(View.VISIBLE);
}else if (datatype.equals("Video")){
    vPath=model.getData();
    holder.vPlayer.setVisibility(View.VISIBLE);
}

更好的解决方案是为每个视图使用回收站视图和不同的视图持有者。

以下是我的一个回收适配器的示例。你可以阅读它们here.

public class ProgramRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

/** 
This is an abstract class that all of my viewholders inherit from. 
This is a contract telling me that any subclasses that inherit from this
base class are required to write their own `public void bind(int position,
Program program);` method. 
*/
abstract class ProgramBaseViewHolder extends RecyclerView.ViewHolder {

    public ProgramBaseViewHolder(View itemView) {
        super(itemView);
    }

    public abstract void bindDataToView(int position, Program program);
}

@Bind和Butterknife.bind是名为Butterknife的视图绑定库的一部分。 “绑定”的使用等同于您的findViewById()来电。

/**
This is the Airtime view that holds airtimes. It is a view holder that
inherits from my base view holder and implements its own version if bind.
*/
class AirtimeViewHolder extends ProgramBaseViewHolder {
    @Bind(R.id.program_airtimes)
    TextView mProgramAirtimes;

    static final int viewType = 0;

    public AirtimeViewHolder(View itemView) {
        super(itemView);
        /**This call to butterknife can be replaced with an
        itemView.findViewById(R.id.yourview) */
        ButterKnife.bind(this, itemView);
    }

    //This is where you set your text and hide or show your views.
    @Override
    public void bindDataToView(int position, Program program) {
        List<Airtime> airtimes = program.getAirtimes();
        if (!airtimes.isEmpty()) {
            mProgramAirtimes.setText(Utils.getFriendlyAirtimes(airtimes));
        } else {
            mProgramAirtimes.setText(
                    Utils.getFriendlyAirTime(program.getAirtime()));
        }
    }
}

/**
This is the Description view that holds descriptions. It is a view holder  
that inherits from my base view holder and implements its own version if    
bind.
*/
class DescriptionViewHolder extends ProgramBaseViewHolder {
    @Bind(R.id.description_card_layout)
    TextView mProgramDescription;

    static final int viewType = 1;

    public DescriptionViewHolder(View itemView) {
        super(itemView);
        ButterKnife.bind(this, itemView);
    }

    @Override
    public void bindDataToView(int position, Program program) {
        mProgramDescription.setText(Html.fromHtml(program.getFullDescription()));
    }
}
//This is another type of view with another different type of layout.
class HostsViewHolder extends ProgramBaseViewHolder {
    @Bind(R.id.card_view_host_name)
    TextView mProgramHostName;

    static final int viewType = 2;

    public HostsViewHolder(View itemView) {
        super(itemView);
        ButterKnife.bind(this, itemView);
    }

    @Override
    public void bindDataToView(int position, Program program) {
        mProgramHostName.setText(program.getHosts().get(position - 2).getDisplayName());
    }
}
//Again another type of view extending my base view.
class CategoriesViewHolder extends ProgramBaseViewHolder {
    @Bind(R.id.program_categories)
    TextView mProgramCategories;
    static final int viewType = 42;

    public CategoriesViewHolder(View itemView) {
        super(itemView);
        ButterKnife.bind(this, itemView);
    }

    @Override
    public void bindDataToView(int position, Program program) {
        List<Category> categoryList = program.getCategories();
        StringBuilder stringBuilder = new StringBuilder();
        for (Category category : categoryList) {
            stringBuilder.append(category.getTitle())
                    .append(" ");
        }
        mProgramCategories.setText(stringBuilder.toString());
    }
}

//This is where the normal looking recycler view code comes in.
private Context mContext;
private LayoutInflater mInflater;
private Program mProgramData;
private int mNextProgramId;

public ProgramRecyclerAdapter(Context context) {
    mContext = context;
    mInflater = LayoutInflater.from(mContext);
}

在您的情况下,您可以通过查看数据来确定视图类型。我将我的视图类型基于位置和一些数据。

/**This method is where I am determining what view type each item in my list 
will be. I wanted a single airtimes view followed by a single description 
view and then X amount of hosts views and a single category view. I return 
position in the third else if because the position helps me determine which 
host name to display in the bindDataToViews call of the HostViewHolder.*/
@Override
public int getItemViewType(int position) {
    if (position == AirtimeViewHolder.viewType) {
        return AirtimeViewHolder.viewType;
    } else if (position == DescriptionViewHolder.viewType) {
        return DescriptionViewHolder.viewType;
    } else if (position > DescriptionViewHolder.viewType
            && position <= DescriptionViewHolder.viewType + getHostsNum()) {
        return position;
    } else {
        return CategoriesViewHolder.viewType;
    }
}

//This method figures out how many hosts will be displayed
private int getHostsNum() {
    if (mProgramData != null) {
        return mProgramData.getHosts().size();
    }
    return 0;
}
// This method determines if I will show a category view or not.
private int getCategoriesNum() {
    if (mProgramData != null && mProgramData.getCategories().size() > 0) {
        return 1;
    }
    return 0;
}

/**This returns haw many items will be in the list. 1 Airtime view, 1 
Description view, x amount of Host views and 0 or 1 category views */
@Override
public int getItemCount() {
    return 2 + getHostsNum() + getCategoriesNum();
}

/** This returns the appropriate View holder for the requested view type that 
was set by getItemViewType(). I pass the inflated parent view and the data.     
*/
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == AirtimeViewHolder.viewType) {
        return new AirtimeViewHolder(mInflater.inflate(R.layout.airtime_card_layout, parent, false));
    } else if (viewType == DescriptionViewHolder.viewType) {
        return new DescriptionViewHolder(mInflater.inflate(R.layout.description_card_layout, parent, false));
    } else if (viewType > DescriptionViewHolder.viewType
            && viewType <= DescriptionViewHolder.viewType + getHostsNum()) {
        return new HostsViewHolder(mInflater.inflate(R.layout.hosts_card_layout, parent, false));
    } else
        return new CategoriesViewHolder(mInflater.inflate(R.layout.categories_card_layout, parent, false));
}

/*This method is what ties everything together. After I ensure that the data 
is not null I call bindDataToView on a ProgramBaseViewHolder. Depending on 
which type of subclass it is will determine which overridden bindData code to 
use. */
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    ProgramBaseViewHolder baseViewHolder = (ProgramBaseViewHolder) holder;
    if (mProgramData != null) {
        baseViewHolder.bindDataToView(position, mProgramData);
    }
}

//This is used to set the data for this program
public void setProgramData(Program program) {
    mProgramData = program;
}

public Program getProgramData() {
    return mProgramData;
}

public boolean isEmpty() {
    return mProgramData == null;
}
}

这是Airtime布局

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/card_margin">  
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    android:orientation="vertical"> 
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/airtimes_label"
        android:textSize="18dp"
        android:textStyle="bold"
        android:textAppearance="@style/TextAppearance.AppCompat.Body2"
        android:layout_marginBottom="4dp"/> 
    <TextView
        android:id="@+id/program_airtimes"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:textAppearance="@style/TextAppearance.AppCompat.Title" />  
</LinearLayout>

这是我的主机布局。你会注意到我没有在这里使用大多数视图,因为这是一个正在进行的应用程序。

    <?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/host_card_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="@dimen/card_margin"
    card_view:cardBackgroundColor="@color/white"
    card_view:cardCornerRadius="2dp">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="8dp" />
        <ImageView
            android:id="@+id/host_image"
            android:layout_width="112dp"
            android:layout_height="112dp"
            android:layout_alignParentLeft="true"
            android:visibility="gone"
            android:layout_centerVertical="true"
            android:layout_marginRight="8dp" />
        <LinearLayout
            android:id="@+id/details"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_toRightOf="@id/host_image"
            android:orientation="vertical">
            <TextView
                android:id="@+id/card_view_host_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="18sp"
                android:layout_margin="16dp"
                android:textAppearance="@style/TextAppearance.AppCompat.Body2"
                android:layout_gravity="left" />
            <TextView
                android:id="@+id/card_view_hosts_programs"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:visibility="gone"
                android:textStyle="bold"
                android:textSize="12sp"
                android:layout_marginBottom="16dp"
                android:layout_gravity="left"/>
        </LinearLayout>
    </RelativeLayout>
</android.support.v7.widget.CardView>

This is the view that is produced with some data. The Categories view is cut off but it is there.