When a user enters a word, it creates Buttons - one Button per letter of the word:
Illustration:
If the user enters "so" it creates 2 Buttons - 's', 'o'
If the user enters "make" it creates 4 Buttons - 'm', 'a', 'k', 'e'
I was having a hard time deciding how I should design this. Ultimately I decided to do the following: Each word is added to a vertical LinearLayout. And for each word, each letter is added to a horizontal LinearLayout. So it's a LinearLayout within a LinearLayout approach.
Here's the code I created which works:
//creates words dynamically
public void makeNewWord(LinearLayout ll, View v, EditText e){
//the horizontal linear layout
LinearLayout linearLayout2 = new LinearLayout(v.getContext());
linearLayout2.setOrientation(LinearLayout.HORIZONTAL);
//the parameters for the horizontal linear layout
LinearLayout.LayoutParams rlp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
//e is the user input
int size = e.getText().toString().length();
for (int i=0; i<size; i++){
final Button dynamicButtons = new Button(v.getContext());
dynamicButtons.setLayoutParams(rlp);
//add the buttons to the horizontal linear layout
linearLayout2.addView(dynamicButtons, rlp);
}
// ll is the vertical linear layout which I created in xml
// so for each entered word, I am adding horizontal linear layouts to my vertical layout
ll.addView(linearLayout2, 0);
}
But now I realized it's probably more efficient using a ListView, especially since I want to make the list of words to be expandable and collapsible. But Is it possible to create the above illustration using a ListView? How would I go about doing so?
I tried creating an ArrayAdapter as follows: ArrayAdapter<LinearLayout> adapter = new ArrayAdapter<LinearLayout>(this, R.id.listview). So basically it would be a ListView of horizontal LinearLayouts. Or should I make an ArrayAdapter of Buttons instead? What is the correct approach?
I can give some idea you can transform this idea in code
1. onTextChanged() method try to get length of text.
2. If you able to get text length then by subString() method get last entered text
3. Then recreate new button instance
You can use a TableLayout for this.
test.xml
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<TableLayout
android:id="#+id/table_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</TableLayout>
</ScrollView>
Activity Code
private TableLayout tableLayout;
private HashMap<String, TableRow> tableRows;
#Override
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.test);
super.onCreate(savedInstanceState);
tableLayout = (TableLayout) findViewById(R.id.table_layout);
tableRows = new HashMap<String, TableRow>();
}
public void addWord(String word) {
if (!tableRows.containsKey(word)) {
TableRow tableRow = new TableRow(this);
for (int i = 0; i < word.length(); i++) {
String letter = String.valueOf(word.charAt(i));
Button btnLetter = new Button(this);
btnLetter.setText(letter);
tableRow.addView(btnLetter);
}
tableRows.put(word, tableRow);
tableLayout.addView(tableRow);
}
}
public void removeWord(String word) {
TableRow tableRow = tableRows.remove(word);
if (tableRow != null) {
tableLayout.removeView(tableRow);
}
}
public void showWord(String word) {
TableRow tableRow = tableRows.get(word);
if (tableRow != null) {
tableRow.setVisibility(View.VISIBLE);
}
}
public void hideWord(String word) {
TableRow tableRow = tableRows.get(word);
if (tableRow != null) {
tableRow.setVisibility(View.GONE);
}
}
Assuming you want a specific button setup, you can inflate an xml button layout dynamically. See here for details.
I would just use a horizontal list view per word.
I you want to be fancy you create a custom layout manager and the new RecyclerView.
Each character of your word would be then a item in your list view. The layout then could be simply a button.
class ListAdapter extends BaseAdapter {
private final Context fContext;
private String mWord;
public ListAdapter(Context context) {
fContext = context;
}
public void updateWord(String word) {
mWord = word;
notifyDataSetChanged();
}
#Override
public int getCount() {
return mWord == null ? 0 : mWord.length();
}
#Override
public String getItem(int position) {
return String.valueOf(mWord.charAt(position));
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Button button;
if (convertView == null) {
button = new Button(parent.getContext());
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
button.setLayoutParams(params);
} else {
button = (Button) convertView;
}
button.setText(getItem(position));
return button;
}
}
On every text change you can then just update the list.
adapter.updateWord();
Be aware the code is just out of my head and i haven't tested this, but should be enough to give you and idea.
Related
I'm currently trying to add a user-defined number of TextViews and EditText to an activity, but can't seem to do it other than hard-coding it by creating a variety of different activities.
The objective of this activity is to take the names of the players, the number of which is relayed by the intent extra from the preceding activity.
I'm trying to add both a TextView saying "Player X: " and an EditText to type the name of the player for each player
I know from this post: How to create a variable number of textviews in android that I have to do this programmatically, however, it does not seem to work for me (the activity remains blank when tested)
I have tried creating a temp LinearLayout to which I add the two views in a for(), still nothing.
Any ideas? Am I on the right track?
Best regards
[EDIT] Code is here :
public class players extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_players);
Bundle extras = new Bundle();
final int numPlayers = extras.getInt("num");
final LinearLayout myLayout = findViewById(R.id.lin_Re);
final ArrayList<Player> players = new ArrayList<>();
int size = numPlayers; // total number of TextViews to add
TextView[] tv = new TextView[size];
TextView temp;
EditText[] et = new EditText[size];
EditText temp2;
LinearLayout temp3;
for (int i = 0; i < size; i++)
{
temp = new TextView(this);
temp2 = new EditText(this);
temp3 = new LinearLayout(this);
temp.setText("Player " + i + " : "); //arbitrary task
// add the textview to the linearlayout
temp3.addView(temp);
temp3.addView(temp2);
tv[i] = temp;
et[i] = temp2;
myLayout.addView(temp3);
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:id="#+id/lin_Re">
</LinearLayout>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/b_send"/>
</LinearLayout>
There is 2 way to achieve this:
1. Use a RecyclerView [Recommended]
2. Add TextView and EditText ( which is nested in a Horizontal LinearLayout) into a Vertical LinearLayout nested in a ScrollView programmatically
The first solution I describe below is quite simple if you're familiar with RecyclerView or ListView, the second solution (your current track) is a bit tricky but still achievable.
Solution 1:
MainActivity
public class MainActivity extends AppCompatActivity {
RecyclerView mPlayerList;
List<String> mPlayerNames;
PlayerAdapter mAdapter;
EditText mInput;
Button mCreateButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPlayerNames = new ArrayList<>();
// setup recycler view
mPlayerList = findViewById(R.id.player_list);
mPlayerList.setLayoutManager(new LinearLayoutManager(this));
mAdapter = new PlayerAdapter();
mPlayerList.setAdapter(mAdapter);
// setup input EditText
mInput = findViewById(R.id.input);
// setup Create button
mCreateButton = findViewById(R.id.create_button);
mCreateButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// clear old player names
mPlayerNames.clear();
// read user input: number of player:
String input = mInput.getText().toString();
int numberOfPlayer;
try {
numberOfPlayer = Integer.parseInt(input);
} catch (NumberFormatException e) {
Toast.makeText(MainActivity.this, "Invalid input!!!", Toast.LENGTH_SHORT).show();
return;
}
for (int i = 0; i < numberOfPlayer; ++i) {
mPlayerNames.add("Player #" + (i + 1));
}
// make change on recycler view
mAdapter.notifyDataSetChanged();
// dismiss keyboard
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mInput.getWindowToken(), 0);
}
});
}
private class PlayerAdapter extends RecyclerView.Adapter<PlayerAdapter.PlayerHolder> {
#Override
public PlayerHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_layout, parent, false);
return new PlayerHolder(v);
}
#Override
public void onBindViewHolder(PlayerHolder holder, int position) {
holder.bind(mPlayerNames.get(position));
}
#Override
public int getItemCount() {
return mPlayerNames.size();
}
public class PlayerHolder extends RecyclerView.ViewHolder {
TextView mPlayerLabel;
EditText mPlayerName;
public PlayerHolder(View itemView) {
super(itemView);
mPlayerLabel = itemView.findViewById(R.id.player_label);
mPlayerName = itemView.findViewById(R.id.player_name);
}
public void bind(String playerName) {
mPlayerLabel.setText(playerName);
mPlayerName.setHint("Name of " + playerName);
}
}
}
}
The sample project can be found here:
https://github.com/raiytu4/stackcase004
I am adding a Customized View using an array.The array elements are intialized by inflating a layout and adding those elements to a ViewGroup as shown in the image.
When I am setting onClickListener in a way to make the clicked view's background as Accent Color It happens but in order to make it mutually exclusive so that once a view is clicked other View's background should become transparent as they were intially I have used the following code But my when I click on the View, my applications stops responding.If my approach is not correct Please suggest me the right way to get desired result.
This should happen:
this should not happen:
if(noOfChild>1) {
for (j = 0; j < noOfChild; j++) {
LayoutInflater inflater = (LayoutInflater) getSystemService(getApplicationContext().LAYOUT_INFLATER_SERVICE);
childButton[j] = (inflater.inflate(R.layout.child_selection_button, null));
childButton[j].setId(j);
children.addView(childButton[j], new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, 1.0f));
childButton[j].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
v.setBackgroundColor(ContextCompat.getColor(MainScreen.this, R.color.dimAccent));
// for (int k =0;k<noOfChild;k++){
// while(k!=v.getId()){
// childButton[k].setBackgroundColor(ContextCompat.getColor(MainScreen.this, R.color.transparent));
// }
// }
}
});
}
}
You can keep a local variable which shows the position of the last selected item. Then in your onClick() method do the switch in the position and the backgroundColor:
private View lastSelected;
//... rest of code ...
//Inside for loop
public void onClick(View v){
if (lastSelected == null){
lastSelected = v;
selectItem(lastSelected);
}
else
{
deselectItem(lastSelected);
lastSelected = v;
selectItem(lastSelected);
}
}
private void selectItem(View v){
v.setBackgroundColor(ContextCompat.getColor(MainScreen.this,R.color.dimAcent));
}
private void deselectItem(View v){
v.setBackgroundColor(ContextCompat.getColor(MainScreen.this, R.color.transparent));
}
I'm probably missing something simple here, but I just can't see it...
So, I have a vanilla ListView a custom adapter and a relative layout.
Currently, items in the listview don't show. However, the custom adapter works, as long as the listview is not in a relative layout (ex: frame layout). On the other side, I can have the listview in a relative layout... as long as I don't use my custom adapter.
public class MainActivity : Activity
{
int count = 1;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
ListView list = new ListView (this);
list.SetBackgroundColor (Android.Graphics.Color.Red);
RelativeLayout rl = new RelativeLayout (this);
rl.SetBackgroundColor (Android.Graphics.Color.Blue);
TextView tv = new TextView (this);
tv.Text = "Some text";
tv.SetBackgroundColor (Android.Graphics.Color.Green);
rl.AddView (tv, new RelativeLayout.LayoutParams (400, 100));
List<RelativeLayout> views = new List<RelativeLayout>();
views.Add (rl);
testAdapter adapter = new testAdapter (views, this);
//ArrayAdapter<string> arrayAdapter = new ArrayAdapter<string> (this, Android.Resource.Layout.SimpleListItem1);
//arrayAdapter.Add ("cell 1");
list.Adapter = adapter;
RelativeLayout listContainer = new RelativeLayout (this);
listContainer.AddView (list, new ViewGroup.LayoutParams (400, 600));
listContainer.SetBackgroundColor (Android.Graphics.Color.LightGray);
this.AddContentView (listContainer, new ViewGroup.LayoutParams (ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent));
}
}
class testAdapter:BaseAdapter{
List<RelativeLayout> views = new List<RelativeLayout>();
Android.Content.Context context;
public testAdapter(List<RelativeLayout> inView, Android.Content.Context cntx){
views = inView;
context = cntx;
}
#region implemented abstract members of BaseAdapter
public override Java.Lang.Object GetItem (int position)
{
return position;
}
public override long GetItemId (int position)
{
return position;
}
public override View GetView (int position, View convertView, ViewGroup parent)
{
RelativeLayout temp;
if (convertView != null) {
temp = (RelativeLayout)convertView;
temp.SetBackgroundColor (Android.Graphics.Color.Green);
} else {
temp = new RelativeLayout (context);
}
temp.RemoveAllViews ();
if (position < views.Count) {
RelativeLayout refView = views [position];
View refParent = (View)refView.Parent;
if (refParent != null)
((ViewGroup)refParent).RemoveView (refView);
temp.AddView (refView);
temp.SetBackgroundColor (Android.Graphics.Color.Green);
}
return (View)temp;
}
public override int Count {
get {
return views.Count;
}
}
#endregion
}
At this point, I only need to change listContainer from a RelativeLayout to a FrameLayout and everything is well. Or I could keep the relative layout and change the adapter to the arrayAdapter that I commented out. I know this is doable but I'm not using any wrap content that usually causes these kind of issues.
Something else: The cell returned by the getView never gets any dimensions. However, if I were to force some dimensions (ex: by adding below temp.SetBackgroundColor (Android.Graphics.Color.Green); something like temp.LayoutParameters = new AbsListView(300, 100);) the green cell will actually appear, but not its content.
Sorry but for a lack of reputation i can't comment, only reply.
Maybe it's not the solution you were looking for but it might work: have you tryed leaving the list in a FrameLayout and placing the FrameLayout in a RelativeLayout?
I am fetching some content from an API which I need to show in android app. The content comes in JSON format, which looks something like this:
{
"items": [
{
"catalog_items": [
{
"date": "23-01-2015",
"content": "Trimmer 1 description",
"name": "Trimmer 1"
},
{
"date": "25-01-2015",
"content": "Trimmer 2 description",
"name": "Trimmer 2"
}
.....
.....
],
"item_category": "Trimmer"
},
{
"catalog_items": [
{
"date": "13-08-2014",
"content": "Shirt description here",
"name": "John Player Shirt"
}
],
"item_category": "Shirts"
},
{
"item_category": "Woolen"
}
],
"pages": [
{
"date": "24-01-2015",
"content": "This is some content about page 1",
"name": "Sample Page title 1"
},
{
"date": "26-01-2015",
"content": "This is some content about page 2",
"name": "Sample Page title 2"
}
]
}
I have to create a dashboard in app which is built up in following manner, based upon above JSON data:
Top Menu
=====================
Trimmers (Gridview)
Trimmer1 Trimmer2
Trimmer3 Trimmer4
======================
Shirts (Gridview)
John Players
======================
Pages (Listview)
Page1
Page2
My Dashboard Fragment fetches this JSON. My Dashboard layout is:
fragment_home.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/relativeLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</RelativeLayout>
and My Dashboard Activity looks like this:
public class HomeFragment extends Fragment {
.....
.....
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_home, container, false);
try {
JSONObject obj = new JSONObject(loadJSONFromAsset("input.json"));
if(obj.has("catagories")) {
JSONArray catag = obj.getJSONArray("items");
for(int i=0; i< catag.length();i++){
JSONObject catitem = catag.getJSONObject(i);
JSONArray posts = catitem.getJSONArray("catalog_items");
for(int j=0;j<posts.length();j++){
JSONObject postitem = posts.getJSONObject(j);
catList.add(postitem.getString("name"));
}
addGridtoLayout(catitem.getString("item_category"),catList);
}
}
} catch (JSONException e){
e.printStackTrace();
}
return rootView;
}
public void addGridtoLayout(String title, ArrayList<String> itemList)
{
RelativeLayout ll = new RelativeLayout(getActivity());
RelativeLayout.LayoutParams parambs = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
ll.setLayoutParams(parambs);
TextView tv = new TextView(getActivity());
tv.setText(title);
tv.setId(R.id.layout1);
RelativeLayout.LayoutParams lay = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
ll.addView(tv, lay);
GridView gridView= new GridView(getActivity());
gridView.setLayoutParams(new GridView.LayoutParams(GridLayout.LayoutParams.MATCH_PARENT, GridLayout.LayoutParams.MATCH_PARENT));
gridView.setNumColumns(GRID_COL_NUM);
gridView.setAdapter(new HomeGridAdapter(getActivity(), itemList));
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.BELOW, tv.getId());
ll.addView(gridView, params);
mainLayout.addView(ll);
}
This is the code only for items (Gridview).
What I am going is basically wrapping each gridview and the title in a relativelayout. And the relative layouts would appear below the former one, starting from first item.
But gridviews are getting stacked over each other and all items are being shown in a single row.
Is there any method so I can define the relativelayouts of each gridview to appear below the previous one? Also, is there any better approach to achieve this? I mean the basic requirement is to generate dynamic number of gridviews. So is generating layout on the fly is only way to do this?
Thanks for reading this long post.
Well, as I mentioned in comment, I made slightly modified code and that works really awesome. So I am going to elaborate what I did. Maybe it could help someone out.
Since I have to generate multiple sectioned Gridviews, I initially thought of generating tables using TableLayout and adding TableRows to it. Each Tablerow would be holding a RelativeView consiting of:
Item's image (from JSON)
Item's name (from JSON)
So, I initially created this code:
HomeFragment.java
public class HomeFragment extends Fragment {
.....
.....
ArrayList<String> itemList;
private JsonHelper jsonHelper;
private static final int GRID_COL_NUM = 3;
TableRow tbh;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_home, container,false);
TableLayout table = (TableLayout) rootView.findViewById(R.id.tableLayout);
table.setStretchAllColumns(true);
table.setShrinkAllColumns(true);
try {
JSONObject obj = new JSONObject(loadJSONFromAsset("input.json"));
if(obj.has("catagories")) {
JSONArray catag = obj.getJSONArray("items");
for(int i=0; i< catag.length();i++){
JSONObject catitem = catag.getJSONObject(i);
JSONArray items = catitem.getJSONArray("catalog_items");
if(items.length() > 0) {
//Add a Grid Title for this category (span=6, full width)
TableRow tbr = addRowTitle(catitem.getString("cat_name"));
table.addView(tbr); //Add this title row to the table
itemList = new ArrayList<>(); //Hold all items for each category
for(int j=0;j<items.length();j++){
JSONObject singleItem = items.getJSONObject(j);
//I am trying to show only 3 items in a row, so add new row after 3
if(j % 3 == 0){
tbh = addRowGrid(); //add new row and repeat so after 3
table.addView(tbh); //Add this to the table, we will fill items below
}
LinearLayout lnr = new LinearLayout(getActivity());
TableRow.LayoutParams params = new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT,TableRow.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER; //Center the items
lnr.setLayoutParams(params);
lnr.setOrientation(LinearLayout.VERTICAL); //As per your need. worked for me
//Item's Image
ImageView itemImg = new ImageView(getActivity()); itemImg.setImageResource(R.drawable.ic_communities); //Default icon for item
TextView itemLabel = new TextView(getActivity());
itemLabel.setText(singleItem.getString("name"));
itemLabel.setTypeface(Typeface.SERIF, Typeface.BOLD);
lnr.addView(postImg,params);
lnr.addView(postLabel,params);
tbh.addView(lnr); //Now we have image and content, wrap in linear layout and add to the row.
itemList.add(singleItem.getString("name")); //This step is not needed for now. I just wrote this to use this array for other purposes.
}
//I am all confused at this point if I have really closed all of curly braces. Please confirm
}
}
}
} catch (JSONException e){
e.printStackTrace();
}
return rootView;
}
public TableRow addRowGrid()
{
return new TableRow(getActivity());
}
public TableRow addRowTitle(String titleb)
{
TableRow rowTitle = new TableRow(getActivity());
rowTitle.setGravity(Gravity.CENTER_HORIZONTAL);
// title column/row
TextView title = new TextView(getActivity());
title.setText(titleb);
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
title.setGravity(Gravity.CENTER);
title.setTypeface(Typeface.SERIF, Typeface.BOLD);
TableRow.LayoutParams params = new TableRow.LayoutParams();
params.span = 6;
rowTitle.addView(title, params);
return rowTitle;
}
...
...
}
This whole code works for a single Table layout defined in layout:
fragment_home:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/card_scrollview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true">
<RelativeLayout
android:id="#+id/relativeLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TableLayout
android:id="#+id/tableLayout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"></TableLayout>
</RelativeLayout>
</ScrollView>
So the whole table is generated on the fly. Worked pretty well. I can attach a click handler to each row to listen, and I might have to set tag for each views to get the clicked item. Its then I thought to more feasible and flexible approach. What if I add the gridview to each row itself, instead of appending LinearLayouts to each TableRows? So, I ended up on modifying my code on this approach:
1. A single TableRow will hold each category title and a second Tablerow will be appended to it, which will hold the gridview.
2. I can now use a Custom Grid Adapter by which I could change layout of each item better, i.e via XML instead of tingling here "on-the-fly" approach.
3. Above point also gained me a clicklistener. So no setting of tag required.
NOTE: The above approach was working as well as below approach is working too (as same). So you can use both
Hence, I create a function that would add title to each category of item and a function to add items to grid, and grid to a row:
HomeFragment.java (gridview version)
public class HomeFragment extends Fragment {
private static final int GRID_COL_NUM = 3;
TableRow tbh;
public HomeFragment(){}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_home, container, false);
TableLayout table = (TableLayout) rootView.findViewById(R.id.tableLayout);
table.setStretchAllColumns(true);
table.setShrinkAllColumns(true);
try {
JSONObject obj = new JSONObject(loadJSONFromAsset("input.json"));
if(obj.has("catagories")) {
JSONArray catag = obj.getJSONArray("catagories");
for(int i=0; i< catag.length();i++) {
JSONObject catitem = catag.getJSONObject(i);
if (catitem.has("items")) {
JSONArray items = catitem.getJSONArray("catalog_items");
if (items.length() > 0) {
//Add row with title catname
TableRow tbr = addRowTitle(catitem.getString("cat_name")); //Add category Name as title
table.addView(tbr); //Same I did previously
ArrayList<String> itemList = new ArrayList<>();
for (int j = 0; j < items.length(); j++) {
JSONObject singleItem = posts.getJSONObject(j);
itemList.add(singleItem.getString("name"));
}
tbh = addGridRow(itemList); //Create Grid of collections and add it to a tablerow
table.addView(tbh); //Add this tablerow to table
}
}
}
}
if(obj.has("pages")) {
JSONArray pages = obj.getJSONArray("pages");
if(pages.length() > 0) {
TableRow tbr = addRowTitle("Pages");
table.addView(tbr);
ArrayList<String> pageList = new ArrayList<>();
for (int i = 0; i < pages.length(); i++) {
JSONObject page = pages.getJSONObject(i);
pageList.add(page.getString("name"));
}
tbh = addGridRow(pageList);
table.addView(tbh);
}
}
} catch (JSONException e){
e.printStackTrace();
}
return rootView;
}
/**
* Now this is a life saver. Gridviews aren't friendly with scrollviews (I might be wrong, but happens with me everytime), especially if generated dynamically. Hence this function calculates height of each item of gridview dynamically and hence sets the total view height adapted from https://stackoverflow.com/a/22555947/1136491
**/
public void setGridViewHeightBasedOnChildren(GridView gridView, int columns) {
ListAdapter listAdapter = gridView.getAdapter();
if (listAdapter == null) {
// pre-condition
return;
}
int totalHeight = 0;
int items = listAdapter.getCount();
int rows = 0;
View listItem = listAdapter.getView(0, null, gridView);
listItem.measure(0, 0);
totalHeight = listItem.getMeasuredHeight();
float x = 1;
if( items > columns ){
x = items/columns;
rows = (int) (x + 1);
totalHeight *= rows;
}
ViewGroup.LayoutParams params = gridView.getLayoutParams();
params.height = totalHeight;
gridView.setLayoutParams(params);
}
public TableRow addGridRow(ArrayList gridItemList)
{
TableRow gridrow = new TableRow(getActivity());
GridView gv = new GridView(getActivity());
TableRow.LayoutParams params = new TableRow.LayoutParams(
TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT);
gv.setLayoutParams(params);
gv.setNumColumns(GRID_COL_NUM); //3 columns grid
gv.setAdapter(new HomeGridAdapter(getActivity(), gridItemList)); //Custom grid adapter I was talking about. See Below
gridrow.addView(gv);
//Thanks for not messing up my gridview inside scrollview
setGridViewHeightBasedOnChildren(gv,GRID_COL_NUM); //3 Columns
return gridrow;
}
//Simply add title above the gridview
public TableRow addRowTitle(String titleb)
{
TableRow rowTitle = new TableRow(getActivity());
rowTitle.setGravity(Gravity.CENTER_HORIZONTAL);
// title column/row
TextView title = new TextView(getActivity());
title.setText(titleb);
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
title.setGravity(Gravity.CENTER);
title.setTypeface(Typeface.SERIF, Typeface.BOLD);
TableRow.LayoutParams params = new TableRow.LayoutParams();
params.span = 6;
rowTitle.addView(title, params);
return rowTitle;
}
....
}
Now my custom Adapter
HomeGridAdapter.java
public class HomeGridAdapter extends BaseAdapter {
ArrayList<String> result;
Context context;
private static LayoutInflater inflater=null;
public HomeGridAdapter(Context context, ArrayList<String> itemList) {
// TODO Auto-generated constructor stub
this.result=itemList;
this.context=context;
inflater = ( LayoutInflater )context.
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return result.size();
}
#Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return position;
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
public class Holder
{
TextView tv;
ImageView img;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
Holder holder=new Holder();
View rowView;
//Layout mentioned below
rowView = inflater.inflate(R.layout.home_grid_layout, null);
holder.tv=(TextView) rowView.findViewById(R.id.textView1);
holder.img=(ImageView) rowView.findViewById(R.id.imageView1);
//Item's Text
holder.tv.setText(result.get(position));
//Item's Image
holder.img.setImageResource(R.drawable.ic_launcher);
//A click listener to each gid item
rowView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Toast.makeText(context, "You Clicked "+result.get(position), Toast.LENGTH_SHORT).show();
}
});
return rowView;
}
}
home_grid_layout.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" >
<ImageView
android:id="#+id/imageView1"
android:layout_gravity="center"
android:layout_width="88dp"
android:layout_height="88dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:src="#drawable/ic_launcher" />
<TextView
android:id="#+id/textView1"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:text="TextView" />
</LinearLayout>
So I am currently using Gridview version of the TableLayout. I might have achieved what I wanted, but I would really welcome any suggestions here. Hope this answer serves someone a great help.
Thanks for bearing this long.
I know it is not possible in Android to scroll grid view horizontally. But what I am doing is adding image buttons dynamically inside horizontal scroll view like this:
public class HorizontalScroller extends Activity {
static int l=0;
private Rect mTempRect = new Rect();
static int r1=0;
static int t=0;
static int b=0;
static int x=0;
static int y=0;
//Button[] b1 = new Button[100];
ImageButton btn[][] = new ImageButton[10][10];
//ImageButton b1 = new ImageButton(this);
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinearLayout rl = (LinearLayout)findViewById(R.id.widget92);
LinearLayout.LayoutParams params1 = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
for(int i=0;i<4;i++)
{
for(int j=0;j<10;j++)
{System.out.println("helo");
/* l=l+100;
r1=r1+100;
t=t+100;
b=b+100;*/
//button();
//ImageButton btn=new ImageButton(this);
/* Rect r = mTempRect;
r.left=10;
r.top=10;
r.right=10;
r.bottom=10;
btn[i][j].getDrawingRect(r);*/
//btn[i][j].setId(j);
Rect r = mTempRect;
r.set(0,0,0,0);
Rect r2 = mTempRect;
r2.set(0,20,0,20);
btn[i][j]=new ImageButton(this);
btn[i][j]. setBackgroundResource(R.drawable.icon);
btn[i][j].setMinimumWidth(20);
btn[i][j].setMinimumHeight(20);
params1.setMargins(5, 5, 5,5);
rl.addView(btn[i][j],params1);
System.out.println("1="+btn[i][j].getTop());
System.out.println("2="+btn[i][j].getLeft());
System.out.println("3="+btn[i][j].getRight());
System.out.println("4="+btn[i][j].getBottom());
}
}
}
}
but I am getting all image buttons in a single line. How can I implement them in a grid like structure?
Implementing a horizontally scrolling GridView involves copying a few of the Android source code classes into your codebase (AdapterView, AbsListView, GridView, ScrollBarDrawable) and adding in code to handle the horizontal code. This is mainly copying some of the code and changing top to left, bottom to right, etc. The main reason for having to copy instead of extending is the final nature of those classes.
I implemented a horizontally scrolling GridView a while ago and finally got around to pushing to github:
https://github.com/jess-anders/two-way-gridview
You can
use a TableLayout inside a HorizontalScrollView, or
stay with your approach with an horizontal LinearLayout but adding vertical LinearLayouts instead of directly the images. E.g., adding three to four images per vertical LinearLayout in portrait, and redrawing to add only two in landscape.
I would try the TableLayout approach first.
PS1: for next time, try to remove all the non-relevant code (the less code is there, the easier is to understand what you did).
PS2: Remember that System.out is usually redirected to /dev/null and thus lost, so I strongly suggest you to use Log.d instead.
Complete example
Adapt this to the onCreate() method or wherever you need it:
public void horizontalScrollGalleryLayout () {
HorizontalScrollView sv = new HorizontalScrollView(this);
LinearLayout llh = new LinearLayout(this);
llh.setOrientation(LinearLayout.HORIZONTAL);
LinearLayout.LayoutParams layoutParamsTV = new LinearLayout.LayoutParams(40, 40);
LinearLayout.LayoutParams layoutParamsLL = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
for (int i=0; i<20; i++) {
LinearLayout llv = new LinearLayout(this);
llv.setOrientation(LinearLayout.VERTICAL);
TestView testView1 = new TestView(this, Color.rgb(i*12, 0, 0));
TestView testView2 = new TestView(this, true, Color.rgb(i*12, i*12, 0));
TestView testView3 = new TestView(this, true, Color.rgb(0, i*12, 0));
llv.addView(testView1, layoutParamsTV);
llv.addView(testView2, layoutParamsTV);
llv.addView(testView3, layoutParamsTV);
llh.addView(llv, layoutParamsLL);
}
sv.addView(llh, layoutParamsLL);
setContentView(sv);
}
I'm using a very simple View as an example:
public class TestView extends View {
Context context;
int color;
public TestView(Context context, int color) {
super(context);
this.context = context;
this.color = color;
}
#Override
public void onDraw (Canvas canvas) {
super.onDraw(canvas);
this.setBackgroundColor(Color.LTGRAY);
Paint paint = new Paint (Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
canvas.drawCircle(20, 20, 20, paint);
}
}
There is a very easy trick.
Rotate the grid view by 270 degree and set number of columns as 2.
Rotate each item to 90 degree (so that the items are displayed as original orientation).
This might be useful for some!!
I have done this way:
activity_main.xml:
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<GridView
android:id="#+id/gridView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</GridView>
</LinearLayout>
</HorizontalScrollView>
MainActivity.java:
GridView gridView = (GridView) findViewById(R.id.gridView);
gridView.setNumColumns(arrayList.size());
GridViewAdapter gridViewAdapter = new GridViewAdapter(mContext, arrayList);
gridView.setAdapter(gridViewAdapter);
// Set dynamic width of Gridview
setDynamicWidth(gridView);
Add below method:
private void setDynamicWidth(GridView gridView) {
ListAdapter gridViewAdapter = gridView.getAdapter();
if (gridViewAdapter == null) {
return;
}
int totalWidth;
int items = gridViewAdapter.getCount();
View listItem = gridViewAdapter.getView(0, null, gridView);
listItem.measure(0, 0);
totalWidth = listItem.getMeasuredWidth();
totalWidth = totalWidth*items;
ViewGroup.LayoutParams params = gridView.getLayoutParams();
params.width = totalWidth;
gridView.setLayoutParams(params);
}
Hope this will help you.
I have already posted this answer here, but both questions are
identical...
There is a nice solution in Android from now on : HorizontalGridView.
1. Gradle dependency
dependencies {
compile 'com.android.support:leanback-v17:23.1.0'
}
2. Add it in your layout
your_activity.xml
<!-- your stuff before... -->
<android.support.v17.leanback.widget.HorizontalGridView
android:layout_width="wrap_content"
android:layout_height="80dp"
android:id="#+id/gridView"
/>
<!-- your stuff after... -->
3. Layout grid element
Create a layout for your grid element ( grid_element.xml ). I have created a simple one with only one button in it.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New Button"
android:id="#+id/button" />
</LinearLayout>
4. Create an adapter
Highly inspired by this link : https://gist.github.com/gabrielemariotti/4c189fb1124df4556058
public class GridElementAdapter extends RecyclerView.Adapter<GridElementAdapter.SimpleViewHolder>{
private Context context;
private List<String> elements;
public GridElementAdapter(Context context){
this.context = context;
this.elements = new ArrayList<String>();
// Fill dummy list
for(int i = 0; i < 40 ; i++){
this.elements.add(i, "Position : " + i);
}
}
public static class SimpleViewHolder extends RecyclerView.ViewHolder {
public final Button button;
public SimpleViewHolder(View view) {
super(view);
button = (Button) view.findViewById(R.id.button);
}
}
#Override
public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(this.context).inflate(R.layout.grid_element, parent, false);
return new SimpleViewHolder(view);
}
#Override
public void onBindViewHolder(SimpleViewHolder holder, final int position) {
holder.button.setText(elements.get(position));
holder.button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(context, "Position =" + position, Toast.LENGTH_SHORT).show();
}
});
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemCount() {
return this.elements.size();
}
}
5. Initialize it in your activity :
private HorizontalGridView horizontalGridView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.your_activity);
horizontalGridView = (HorizontalGridView) findViewById(R.id.gridView);
GridElementAdapter adapter = new GridElementAdapter(this);
horizontalGridView.setAdapter(adapter);
}
Use recyclerview with setting its gridlayout as layout manager and set it to horizontal scroll
your recycle view.setLayoutManager(new GridLayoutManager(getActivity(),2, LinearLayoutManager.HORIZONTAL, false))
here 2 is the column span for grid