3

I build app with gridView gallery inside dialog. All gallery works good but now I want separate some photos from another (need to create some kind of headers). For example I have on my SD card folder with some photos named : 1v1.jpg / 1v2.jpg / 1v3.jpg / 2v1.jpg / 2v2.jpg, now I want to display it in my gridView gallery (let's assume that it have 2 columns)

How it should look:

1

1v1.jpg 1v2.jpg

1v3.jpg

2

2v1.jpg 2v2.jpg

etc

For now I have gallery with only photos

Some code :

GridAdapter :

public class GridAdapter extends BaseAdapter {

    Context mContext;
    ArrayList<File> listFiles;

    public GridAdapter(Context context, ArrayList<File> files) {

        this.mContext = context;
        this.listFiles = files;

    }

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

    @Override
    public Object getItem(int position) {
        return listFiles.get(position);
    }

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if(convertView == null)
        {
                convertView = LayoutInflater.from(mContext).inflate(R.layout.my_grid, parent, false);
        }

        final ImageView iv = (ImageView) convertView.findViewById(R.id.imageView);

       Glide.with(mContext)
                .load(listFiles.get(position).getAbsolutePath()) //path to picture
                .into(iv);


        return convertView;
    }

} //end of gridadapter

//rest code

public ArrayList<File> photoList;
public ArrayList<String> albumList;

photoList = imageReader(photoDir);
albumList = albumReader(photoDir);

//function to get all file paths (works)
    private ArrayList<File> imageReader(File root)
    {
        ArrayList<File> a = new ArrayList<>();
        File[] files = root.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {                 
                a.addAll(imageReader(file));
            } else {
                if (file.getName().length() == 14)
                {
                    a.add(file);
                }
            }
        }
        return a;
    }
        //function to get all headers name (works)
        private ArrayList<String> albumReader(File root)
        {
            ArrayList<String> pages = new ArrayList<>();
            File[] files = root.listFiles();
            for (File file : files)
            {
                String photoName;
                String temp = "";
                photoName =  file.getName();
                if(photoName.length()==14)
                {
                    photoName = photoName.substring(0, 4);        
                    if (!temp.equals(photoName))
                    {
                        if(pages.isEmpty() || !pages.contains(photoName))
                        {
                            pages.add(photoName);
                            temp = photoName;
                        }
                        else
                        {
                            break;
                        }
                    }
                }

            }

            return pages;
        }

public void firstChoiceDialogGallery() {

        inflater = this.getLayoutInflater();

        // Dialog layout
        v = inflater.inflate(R.layout.dialog_choice, null);

        // Get gridView from dialog_choice
        gV = (GridView) v.findViewById(R.id.gridView);

        // GridAdapter (Pass context and files list)
        GridAdapter adapter = new GridAdapter(this, photoList);



        // Set adapter
        gV.setAdapter(adapter);

        final AlertDialog.Builder builder2 = new AlertDialog.Builder(this);
        builder2.setTitle("MY GALLERY");

        builder2.setView(v);
        builder2.setPositiveButton("NEXT", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        }).setNegativeButton("BACK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

            }
        });

        builder2.setCancelable(false);
        final AlertDialog dialog = builder2.create();
        dialog.show();
    }

XMLs : grid_item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <ImageView
        android:layout_width="@integer/width"
        android:layout_height="@integer/height"
        android:adjustViewBounds="true"
        android:id="@+id/imageView"
        android:layout_margin="5dp"
        android:layout_centerVertical="true"
        android:layout_alignParentEnd="true" />

</RelativeLayout>

grid.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/dialog_choice"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <GridView
        android:id="@+id/gridView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:numColumns="@integer/column_count"
        android:clickable="true"/>


</RelativeLayout>

As you see I wrote function to get header names, gallery works, but now I don't have Idea how to place this names in specific places and how to do that. I was thinking about put it to my existing adapter but it doesnt't work

P.S I read about StickyGridHeaders so giving here link to lib source isn't answer, because already know about it. Problem is that I'm not sure that this is what I want + I don't have idea how to implement it this existing code

  • You must use RecyclerView. and try this [link](https://gist.github.com/gabrielemariotti/e81e126227f8a4bb339c) – Moinkhan May 24 '17 at 04:41

4 Answers4

2

Need to create a custom adapter called item_row.xml or whatever you want

item_row.xml

inside the layout define your desired view, in your case you have an image view and a text view from what I see:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="105dp"
android:layout_height="120dp">
<ImageView
    android:layout_width="70dp"
    android:layout_height="70dp"
    android:id="@+id/ImgItem"
    android:layout_marginTop="7dp"
    android:layout_gravity="center"/>
<TextView
    android:id="@+id/textItem"
    android:layout_width="120dp"
    android:layout_height="25dp"
    android:textSize="16sp"
    android:layout_marginBottom="3dp"
    android:layout_gravity="center"
    android:textColor="#182b4b"
    android:gravity="center" />
</LinearLayout>

now, create your adapter class, here you indicate the adapter to inflate the view you just created (item_row,xml):

rowitem.java

make sure extends from

ArrayAdapter

public class rowitem extends ArrayAdapter<String> {

private final Activity context;
private String[] nameItem;
private Bitmap[] iconsItem;

public rowitem(Activity context, String[] nameItem, Bitmap[] iconsItem) {
    super(context, R.layout.adapter_sketchmenuside, nameItem);
    // TODO Auto-generated constructor stub

    this.context = context;
    this.nameItem = nameItem;
    this.iconsItem = iconsItem;
}

public View getView(int posicion, View view, ViewGroup parent){
    LayoutInflater inflater = context.getLayoutInflater();
//inflate the item_row.xml view
    View rowView = inflater.inflate(R.item_row,null, 
true);

    TextView txtTitle = (TextView) rowView.findViewById(R.id.textItem);
    ImageView imageView = (ImageView) rowView.findViewById(R.id.ImgItem);

// Do your stuff
}

And as your last step, set your new custom adapter to your gridview in wherever you are declaring it:

GridView myGrid = (GridView) view.findViewById(R.id.GrdCategory);

//in wherever you want, set the adapter:
myGrid.setAdapter(new rowitem(getActivity(), 
               namesArray, //here you send an Array with Strings for the names of the images
               imgArray, // here you send an array with the images you want to load
));

That will do, any questions let me know, if correct please mark as correct answer. Happy Coding!

Jhonycage
  • 759
  • 3
  • 16
  • 36
  • 1
    I may be missing something but I don't think this is what the OP wants. They want 1 header/title for multiple pics. This looks like it does 1 header/title for each pic. – Gary99 May 25 '17 at 13:33
  • I think he wants to put the name of each picture as header – Jhonycage May 25 '17 at 13:51
  • @Juanca You are wrong. I wrote example where I clearly told that I need header for some pictures it can be in some case one photo in other case 20 images. Anyway Now I'm doing all it different way. –  May 25 '17 at 18:42
  • he wants images with header like as we seen in gallery with date section – Manjeet Singh Goyal Aug 15 '20 at 11:35
1

I suppose you need to use different item View Type. And based on this you can configure diferrent layouts for header and base items. This should be aplied for Recycler, based on RecyclerView doc with Layout Manager - StaggeredGridLayoutManager.

CL.
  • 173,858
  • 17
  • 217
  • 259
GensaGames
  • 5,538
  • 4
  • 24
  • 53
0

There´s a great way to achieve this behaviour/layout. Try something like this:

sectionableAdapter:

public abstract class SectionableAdapter extends BaseAdapter {

public static final int MODE_VARY_WIDTHS = 0;
public static final int MODE_VARY_COUNT = 1;

private LayoutInflater inflater;
private int rowResID;
private int headerID;
private int itemHolderID;
private int colCount;
private int sectionsCount;
private int resizeMode;
private ViewGroup measuredRow;

public SectionableAdapter(LayoutInflater inflater, int rowLayoutID, int headerID, int itemHolderID)
{
    this(inflater, rowLayoutID, headerID, itemHolderID, MODE_VARY_WIDTHS);
}

/**
 * Constructor.
 * @param inflater inflater to create rows within the grid.
 * @param rowLayoutID layout resource ID for each row within the grid.
 * @param headerID resource ID for the header element contained within the grid row.
 * @param itemHolderID resource ID for the cell wrapper contained within the grid row. This View must only contain cells.
 */
public SectionableAdapter(LayoutInflater inflater, int rowLayoutID, int headerID, int itemHolderID, int resizeMode)
{
    super();
    this.inflater = inflater;
    this.rowResID = rowLayoutID;
    this.headerID = headerID;
    this.itemHolderID = itemHolderID;
    this.resizeMode = resizeMode;
    // Determine how many columns our row holds.
    View row = inflater.inflate(rowLayoutID, null);
    if (row == null)
        throw new IllegalArgumentException("Invalid row layout ID provided.");
    ViewGroup holder = (ViewGroup)row.findViewById(itemHolderID);
    if (holder == null)
        throw new IllegalArgumentException("Item holder ID was not found in the row.");
    if (holder.getChildCount() == 0)
        throw new IllegalArgumentException("Item holder does not contain any items.");
    colCount = holder.getChildCount();
    sectionsCount = getSectionsCount();
}

/**
 * Returns the total number of items to display.
 */
protected abstract int getDataCount();

/**
 * Returns the number of sections to display.
 */
protected abstract int getSectionsCount();

/**
 * @param index the 0-based index of the section to count.
 * @return the number of items in the requested section.
 */
protected abstract int getCountInSection(int index);

/**
 * @param position the 0-based index of the data element in the grid.
 * @return which section this item belongs to.
 */
protected abstract int getTypeFor(int position);

/**
 * @param section the 0-based index of the section.
 * @return the text to display for this section.
 */
protected abstract String getHeaderForSection(int section);

/**
 * Populate the View and attach any listeners.
 * @param cell the inflated cell View to populate.
 * @param position the 0-based index of the data element in the grid.
 */
protected abstract void bindView(View cell, int position);

/**
 * Perform any row-specific customization your grid requires. For example, you could add a header to the
 * first row or a footer to the last row.
 * @param row the 0-based index of the row to customize.
 * @param convertView the inflated row View.
 */
protected void customizeRow(int row, View rowView)
{
    // By default, does nothing. Override to perform custom actions.
}

@Override
public int getCount()
{
    int totalCount = 0;
    for (int i = 0; i < sectionsCount; ++i)
    {
        int count = getCountInSection(i);
        if (count > 0)
            totalCount += (getCountInSection(i)-1) / colCount + 1;
    }
    if (totalCount == 0)
        totalCount = 1;
    return totalCount;
}

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

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    int realPosition = 0;
    int viewsToDraw = 0;
    int rows = 0;
    int totalCount = 0;
    for (int i = 0; i < sectionsCount; ++i)
    {
        int sectionCount = getCountInSection(i);
        totalCount += sectionCount;
        if (sectionCount > 0 && position <= rows + (sectionCount - 1) / colCount)
        {
            realPosition += (position - rows) * colCount;
            viewsToDraw = (int)(totalCount - realPosition);
            break;
        }
        else
        {
            if (sectionCount > 0)
            {
                rows += (int)((sectionCount - 1) / colCount + 1);
            }
            realPosition += sectionCount;
        }
    }
    if (convertView == null)
    {
        convertView = inflater.inflate(rowResID, parent, false);
        if (measuredRow == null && resizeMode == MODE_VARY_COUNT)
        {
            measuredRow = (ViewGroup)convertView;
            // In this mode, we need to learn how wide our row will be, so we can calculate
            // the number of columns to show.
            // This listener will notify us once the layout pass is done and we have our
            // measurements.
            measuredRow.getViewTreeObserver().addOnGlobalLayoutListener(layoutObserver);
        }
    }
    int lastType = -1;
    if (realPosition > 0)
        lastType = getTypeFor(realPosition-1);
    if (getDataCount() > 0)
    {
        TextView header = (TextView)convertView.findViewById(headerID);
        int newType = getTypeFor(realPosition);
        if (newType != lastType)
        {
            header.setVisibility(View.VISIBLE);
            header.setText(getHeaderForSection(newType));

        }
        else
        {
            header.setVisibility(View.GONE);
        }
    }
    customizeRow(position, convertView);

    ViewGroup itemHolder = (ViewGroup)convertView.findViewById(itemHolderID);
    for (int i = 0; i < itemHolder.getChildCount(); ++i)
    {
        View child = itemHolder.getChildAt(i);
        if (i < colCount && i < viewsToDraw && child != null)
        {
            bindView(child, realPosition + i);
            child.setVisibility(View.VISIBLE);
        }
        else if (child != null)
        {
            child.setVisibility(View.INVISIBLE);
        }
    }
    return convertView;
}

private ViewTreeObserver.OnGlobalLayoutListener layoutObserver = new ViewTreeObserver.OnGlobalLayoutListener() {

    // The better-named method removeOnGlobalLayoutListener isn't available until a later API version.
    @SuppressWarnings("deprecation")
    @Override
    public void onGlobalLayout() {
        if (measuredRow != null)
        {
            int rowWidth = measuredRow.getWidth();
            ViewGroup childHolder = (ViewGroup)measuredRow.findViewById(itemHolderID);
            View child = childHolder.getChildAt(0);
            int itemWidth = child.getWidth();
            if (rowWidth > 0 && itemWidth > 0)
            {
                colCount = rowWidth / itemWidth;
                // Make sure this listener isn't called again after we layout for the next time.
                measuredRow.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                // The grid will now update with the correct column count.
                notifyDataSetChanged();
            }
        }
    }
};

}

BookcaseAdapter:

public class BookcaseAdapter extends SectionableAdapter implements
    View.OnClickListener {

// For simplicity, we hard-code the headers and data. In an actual app, this
// can come from the network, the filesystem, SQLite, or any of the 
// usual suspects.
static final String[] AUTHORS = new String[] { "Roberto Bola–o",
        "David Mitchell", "Haruki Murakami", "Thomas Pynchon" };
private static final String[][] BOOKS = new String[][] {
        { "The Savage Detectives", "2666" },
        { "Ghostwritten", "number9dream", "Cloud Atlas",
                "Black Swan Green", "The Thousand Autumns of Jacob de Zoet" },
        { "A Wild Sheep Chase",
                "Hard-Boiled Wonderland and the End of the World",
                "Norwegian Wood", "Dance Dance Dance",
                "South of the Border, West of the Sun",
                "The Wind-Up Bird Chronicle", "Sputnik Sweetheart",
                "Kafka on the Shore", "After Dark", "1Q84" },
        { "V.", "The Crying of Lot 49", "Gravity's Rainbow", "Vineland",
                "Mason & Dixon", "Against the Day", "Inherent Vice" } };

private Activity activity;

public BookcaseAdapter(Activity activity, LayoutInflater inflater,
        int rowLayoutID, int headerID, int itemHolderID, int resizeMode) {
    super(inflater, rowLayoutID, headerID, itemHolderID, resizeMode);
    this.activity = activity;
}

@Override
public Object getItem(int position) {
    for (int i = 0; i < BOOKS.length; ++i) {
        if (position < BOOKS[i].length) {
            return BOOKS[i][position];
        }
        position -= BOOKS[i].length;
    }
    // This will never happen.
    return null;
}

@Override
protected int getDataCount() {
    int total = 0;
    for (int i = 0; i < BOOKS.length; ++i) {
        total += BOOKS[i].length;
    }
    return total;
}

@Override
protected int getSectionsCount() {
    return BOOKS.length;
}

@Override
protected int getCountInSection(int index) {
    return BOOKS[index].length;
}

@Override
protected int getTypeFor(int position) {
    int runningTotal = 0;
    int i = 0;
    for (i = 0; i < BOOKS.length; ++i) {
        int sectionCount = BOOKS[i].length;
        if (position < runningTotal + sectionCount)
            return i;
        runningTotal += sectionCount;
    }
    // This will never happen.
    return -1;
}

@Override
protected String getHeaderForSection(int section) {
    return AUTHORS[section];
}

@Override
protected void bindView(View convertView, int position) {
    String title = (String) getItem(position);
    TextView label = (TextView) convertView
            .findViewById(R.id.bookItem_title);
    label.setText(title);
    convertView.setOnClickListener(this);
}

@Override
public void onClick(View v) {
    Intent i = new Intent(Intent.ACTION_SEARCH);
    TextView label = (TextView) v.findViewById(R.id.bookItem_title);
    String text = label.getText().toString();
    i.putExtra(SearchManager.QUERY, text);
    activity.startActivity(i);
}

}

SectionedGridActivity:

  public class SectionedGridActivity extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_sectioned_grid);

    ListView list = (ListView) findViewById(R.id.sectionedGrid_list);
    // Switch between these to see the two different types of resizing available.
    BookcaseAdapter adapter = new BookcaseAdapter(this,
            getLayoutInflater(), R.layout.book_row, R.id.bookRow_header,
            R.id.bookRow_itemHolder, SectionableAdapter.MODE_VARY_WIDTHS);
//      BookcaseAdapter adapter = new BookcaseAdapter(this,
//              getLayoutInflater(), R.layout.book_row_vary_columns, R.id.bookRow_header,
//              R.id.bookRow_itemHolder, SectionableAdapter.MODE_VARY_COUNT);
    list.setAdapter(adapter);
    list.setDividerHeight(0);
}

}

And all this layouts to work with it:

activity_sectioned_grid.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
 >

<ListView
    android:id="@+id/sectionedGrid_list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:listSelector="@android:color/transparent"
    />

</RelativeLayout>

book_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
android:layout_height="200dp"
android:layout_weight="1"
android:orientation="vertical" 
android:background="@drawable/gradients"
android:layout_margin="8dp"
>
<TextView
    android:id="@+id/bookItem_title"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:textColor="#ffffff"
    android:padding="8dp"
    />
</LinearLayout>

book_item_vary_columns.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="190dp"
android:layout_height="190dp"
android:orientation="vertical" 
android:padding="8dp"
>
<TextView
    android:id="@+id/bookItem_title"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:textColor="#ffffff"
    android:padding="8dp"
    android:background="@drawable/gradients"
    />
</LinearLayout>

book_row.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView 
    android:id="@+id/bookRow_header"
    style="@android:style/TextAppearance.Holo.Large"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="8dp"
    android:visibility="gone"
    />
<LinearLayout 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:id="@+id/bookRow_itemHolder"
    >
    <include layout="@layout/book_item"/>
    <include layout="@layout/book_item"/>
</LinearLayout>

</LinearLayout>

book_row_vary_columns.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView 
    android:id="@+id/bookRow_header"
    style="@android:style/TextAppearance.Holo.Large"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="8dp"
    android:visibility="gone"
    />
<LinearLayout 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:id="@+id/bookRow_itemHolder"
    >
    <include layout="@layout/book_item_vary_columns"/>
    <include layout="@layout/book_item_vary_columns"/>
    <include layout="@layout/book_item_vary_columns"/>
    <include layout="@layout/book_item_vary_columns"/>
    <include layout="@layout/book_item_vary_columns"/>
    <include layout="@layout/book_item_vary_columns"/>
    <include layout="@layout/book_item_vary_columns"/>
    <include layout="@layout/book_item_vary_columns"/>
    <include layout="@layout/book_item_vary_columns"/>
    <include layout="@layout/book_item_vary_columns"/>
</LinearLayout>

</LinearLayout>

Note that this code isn´t mine, it just fits this question and I´ve researched this information before. Try searching google for sectioned grid - you will find a demo apk.

Curious Mind
  • 659
  • 10
  • 26
0

ok, for such a layout you need basically different gridviews for different images section e.g.

GridView 1 for 1v1.png 1v2.png and so on

GridView 2 for 2v1.png 2v2.png and so on

And these gridviews can then be seperated by a Textview Header.

So To implement this you can make the parent layout as a Listview(instead of gridview as you are taking).

And in this listview the ItemRow can contain a Textview and a gridview below it.

So Listview will fill this in the exact layout as you want. If you dont want any other library e.g. as suggested by above answers then it can be done easily.

So the layouts will be:

parent layout:

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
    <ListView
        android:id="@+id/lv_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    </RelativeLayout>

ANnd the item for this listview will contain:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
    android:id="@+id/tv_header"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

<GridView
    android:id="@+id/gv_items"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:numColumns="2" />

and finally to populate the gridview for the images, the third layout will be :

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">


<ImageView
    android:layout_width="@integer/width"
    android:layout_height="@integer/height"
    android:adjustViewBounds="true"
    android:id="@+id/imageView"
    android:layout_margin="5dp"
    android:layout_centerVertical="true"
    android:layout_alignParentEnd="true" />

</RelativeLayout>
Anmol
  • 448
  • 2
  • 6