问题
I've seen many solutions about how to implement a gridview header that scrolls along with the rest of the grid.
Most of them consist in creating a listlayout or relativelayout with a header view and a gridview all inside a scrollview. This solution has the problem that the scrollview doesn't know the size of the grid so to overcome that you need to extend grid view like here: https://stackoverflow.com/a/4536955/751180
But the problem is that but doing so you are forcing the gridview to render all the items at once without recycling its cells. This will probably cause the app to crash due to heavy memory usage specially if the views contain images.
Other people are using Listviews and calculating how many columns can be placed depending on the screen size. I personally would like to continue using a grid view.
Has anyone ever implemented a gridview header using a different approach?
回答1:
I've spend a lot of time trying to set correct header to GridView. No sucess. It seems, that implementing custom GridView (inherited from ListView) is the only reasonable way. Here is an example of such GridView with headers and footers: https://github.com/SergeyBurish/HFGridView
回答2:
You can try to use the following library https://github.com/maurycyw/HeaderGridView It may be helpful.
回答3:
I did custom grid view with header of Images from sdcard location source. My entire project i am placing below. If it's help full to any one please vote me to increase reputations.
public class MainActivity extends Activity {
private File file;
LinearLayout linear;
ArrayList<Bitmap> listbitmap;
String[] folderlist ;
ExpandableHeightGridView gridview;
LinearLayout linearhere;
String imageInSD = Environment.getExternalStorageDirectory().getAbsolutePath() +"/Hi";
ArrayList<String> mylist = new ArrayList<String>();
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
linear = (LinearLayout) findViewById(R.id.linear);
File imagesSource = new File(imageInSD);
File[] dictoryFiles = imagesSource.listFiles();
for(int i =0; i<dictoryFiles.length;i++){
mylist.add(dictoryFiles[i].getName());
}
folderlist = mylist.toArray(new String[mylist.size()]);
linearhere = new LinearLayout(getApplicationContext());
linearhere.setOrientation(LinearLayout.VERTICAL);
for (int k = 0; k < folderlist.length; k++) {
TextView textview = new TextView(getApplicationContext());
textview.setTextColor(Color.BLACK);
textview.setTextSize(20);
textview.setText(folderlist[k]);
gridview = new ExpandableHeightGridView(getApplicationContext());
gridview.setId(Calendar.SECOND);
gridview.setLayoutParams(new GridView.LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT));
gridview.setBackgroundColor(Color.WHITE);
gridview.setNumColumns(3);
gridview.setColumnWidth(GridView.AUTO_FIT);
gridview.setVerticalSpacing(5);
gridview.setHorizontalSpacing(5);
gridview.setStretchMode(GridView.STRETCH_COLUMN_WIDTH);
listbitmap = new ArrayList<Bitmap>();
file = new File(imageInSD + "/" + folderlist[k]);
File list[] = file.listFiles();
for (int i = 0; i < list.length; i++) {
String sp=list[i].getName();
//if(sp.startsWith("123")){
//textview.setText(folderlist[k]);
File image = new File(imageInSD + "/"+folderlist[k]+"/" + list[i].getName());
InputStream fis = null;
try {
fis = new FileInputStream(image.getAbsolutePath());
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bm = BitmapFactory.decodeStream(fis, null, options);
listbitmap.add(bm);
//}
}
gridview.setAdapter(new CustomGridAdapter(getApplicationContext(),
listbitmap));
gridview.setExpanded(true);
linearhere.addView(textview);
linearhere.addView(gridview);
}
linear.addView(linearhere);
}
}
======================
class2
======================
public class ExpandableHeightGridView extends GridView
{
boolean expanded = false;
public ExpandableHeightGridView(Context context)
{
super(context);
}
public ExpandableHeightGridView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public ExpandableHeightGridView(Context context, AttributeSet attrs,
int defStyle)
{
super(context, attrs, defStyle);
}
public boolean isExpanded()
{
return expanded;
}
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
// HACK! TAKE THAT ANDROID!
if (isExpanded())
{
// Calculate entire height by providing a very large height hint.
// View.MEASURED_SIZE_MASK represents the largest height possible.
int expandSpec = MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
else
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
public void setExpanded(boolean expanded)
{
this.expanded = expanded;
}
}
======================
class 3:
======================
public class CustomGridAdapter extends BaseAdapter {
private Context context;
private final ArrayList<Bitmap> gridValues;
// Constructor to initialize values
public CustomGridAdapter(Context context, ArrayList<Bitmap> gridValues) {
this.context = context;
this.gridValues = gridValues;
}
@Override
public int getCount() {
// Number of times getView method call depends upon gridValues.length
return gridValues.size();
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int id) {
return id;
}
// Number of times getView method call depends upon gridValues.length
public View getView(int position, View convertView, ViewGroup parent) {
// LayoutInflator to call external grid_item.xml file
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridView;
if (convertView == null) {
gridView = new View(context);
// get layout from grid_item.xml ( Defined Below )
gridView = inflater.inflate(R.layout.grid_item, null);
ImageView imageView = (ImageView) gridView
.findViewById(R.id.grid_item_image);
imageView.setImageBitmap(gridValues.get(position));
} else {
gridView = (View) convertView;
}
return gridView;
}
}
=========================
activity_main.xml Code:
=========================
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:id="@+id/linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#D8D8D8"
android:orientation="vertical" >
</LinearLayout>
</ScrollView>
==========================
grid_item.xml code:
==========================
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp" >
<ImageView
android:id="@+id/grid_item_image"
android:layout_width="180dp"
android:layout_margin="5dp"
android:layout_height="150dp"
>
</ImageView>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="0dp" >
</LinearLayout>
</LinearLayout>
回答4:
I'm a little late to answer but in my experience this is possible through the RecyclerView api using GridLayoutManager.SpanSizeLookup helper class. For example, you can do the following in your Activity/Fragment.
mRecyclerView = (RecyclerView) view.findViewById(R.id.your_recycler_view);
mLayoutManager = new GridLayoutManager(mContext, 2);
mLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
if (position == 0) {
return 2;
} else {
return 1;
}
}
});
mRecyclerView.setLayoutManager(mLayoutManager);
mAdapter = new RecyclerAdapter();
mRecyclerView.setAdapter(mAdapter);
If the your at the beginning of the grid (position == 0), your ViewHolder will get the maximum column span, which is 2 in this situation. In your adapter class you will want to specify constants for your view types of header and items and override the int getItemViewType(int position)
method to return the view type based on the adapter position. Then in RecyclerView.ViewHolder onCreateViewHolder(ViewGroup container, int viewType)
inflate the appropriate layout based on the current viewType.
Hopefully this helps future readers. Surprisingly, I haven't seen this solution around for similar questions on GridView headers.
来源:https://stackoverflow.com/questions/16860872/android-gridview-header-solution-with-adapter-recycling-cells