RecyclerView - dynamic height without shifting elements - java

What I need:
I tried doing this using a RecyclerView that sets the height of the item dynamically based on the model. I am using
Layout:
<?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/card_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
card_view:cardUseCompatPadding="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white"
android:orientation="vertical"
android:gravity="center|bottom"
android:layout_weight="0">
<TextView
android:id="#+id/teacher"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:padding="5dp"
android:textColor="#android:color/black"
android:textSize="16sp" />
<TextView
android:id="#+id/room"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/teacher"
android:padding="5dp"
android:textColor="#android:color/black"
android:textSize="16sp" />
<TextView
android:id="#+id/subject"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#1976D2"
android:gravity="center_horizontal"
android:layout_alignParentBottom="true"
android:paddingBottom="8dp"
android:paddingTop="8dp"
android:textColor="#ffffff"
android:textSize="13sp"
android:maxLines="3"
android:lines="1" />
</RelativeLayout>
</android.support.v7.widget.CardView>
Adapter-code:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SampleViewHolders {
val layoutView = LayoutInflater.from(parent.context).inflate(
R.layout.rv_grid, null)
layoutView.getViewTreeObserver().addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
val lp = layoutView.getLayoutParams()
if (lp is StaggeredGridLayoutManager.LayoutParams) {
val sglp = lp as StaggeredGridLayoutManager.LayoutParams
when (viewType) {
1 -> sglp.isFullSpan = false
2 -> {
sglp.height = layoutView.height * 2;
layoutView.refreshDrawableState();
}
3 -> sglp.height = layoutView.height * 3;
}
layoutView.setLayoutParams(sglp)
val lm = (parent as RecyclerView).layoutManager as StaggeredGridLayoutManager
lm.invalidateSpanAssignments()
}
layoutView.getViewTreeObserver().removeOnPreDrawListener(this)
return true
}
})
return ViewHolder(layoutView)
}
Where it does multiplies with the number of hours the meeting takes.
The problem is that if there is a one-hour meeting after a 2-hour meeting, it gets shifted to the next day.
See:
With 2-hour meetings:
With 1-hour meetings:
How can I fix this in the adapter/how can I adapt the height without shifting the elements?

Import the library into your project.
Grab via maven
<dependency>
<groupId>com.github.alamkanak</groupId>
<artifactId>android-week-view</artifactId>
<version>1.2.6</version>
<type>aar</type>
</dependency>
Grab via gradle
compile 'com.github.alamkanak:android-week-view:1.2.6'
Add WeekView in your xml layout.
<com.alamkanak.weekview.WeekView
android:id="#+id/weekView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:eventTextColor="#android:color/white"
app:textSize="12sp"
app:hourHeight="60dp"
app:headerColumnPadding="8dp"
app:headerColumnTextColor="#8f000000"
app:headerRowPadding="12dp"
app:columnGap="8dp"
app:noOfVisibleDays="3"
app:headerRowBackgroundColor="#ffefefef"
app:dayBackgroundColor="#05000000"
app:todayBackgroundColor="#1848adff"
app:headerColumnBackground="#ffffffff"/>
Write the following code in your java file.
/
/ Get a reference for the week view in the layout.
mWeekView = (WeekView) findViewById(R.id.weekView);
// Set an action when any event is clicked.
mWeekView.setOnEventClickListener(mEventClickListener);
// The week view has infinite scrolling horizontally. We have to provide the events of a
// month every time the month changes on the week view.
mWeekView.setMonthChangeListener(mMonthChangeListener);
// Set long press listener for events.
mWeekView.setEventLongPressListener(mEventLongPressListener);
Implement WeekView.MonthChangeListener, WeekView.EventClickListener, WeekView.EventLongPressListener according to your need.
Provide the events for the WeekView in WeekView.MonthChangeListener.onMonthChange() callback. Please remember that the calendar pre-loads events of three consecutive months to enable lag-free scrolling.
MonthLoader.MonthChangeListener mMonthChangeListener = new MonthLoader.MonthChangeListener() {
#Override
public List<WeekViewEvent> onMonthChange(int newYear, int newMonth) {
// Populate the week view with some events.
List<WeekViewEvent> events = getEvents(newYear, newMonth);
return events;
}
};

Related

What's simple code/effective code to create CheckBox Programmatically in BottomSheetDialog when click every item in RecyclerView in Android (Java)

I have 100+ item in RecyclerView. these days I have tried to create simple code/effective code to create Checkbox in BottomSheetDialog Programmatically but no success.
My goal :
Every item click in RecyclerView will show BottomSheetDialog with different amount checkbox. example :
when click item[0] in RecyclerView will open BottomSheetDialog with 7 checkboxes, if click item[1] in RecyclerView will open BottomSheetDialog with 286 checkboxes, or if click item [2] will open BottomSheetDialog with 200 checkboxes, and so on.
This image will describe, what I meant.
I think my goal is possible instead I create 100+ xml for every item in RecyclerView. Using for looping, array and other... if no simple/effective code for programmatically create checkbox, never mind I will create 100+ xml for every itemListener at RecyclerView
What I have tried :
I successed add single checkbox, but failed to add other checkbox. Here is my code :
Note : I commented for looping because it didn't work. Oke, thanks all :)
#Override
public void onClick(View v) {
int itemPosition = recyclerViewSurat.getChildLayoutPosition(v);
String namaSurat = suratList.get(itemPosition).getNamaSurat();
// Toast.makeText(context, namaSurat, Toast.LENGTH_SHORT).show();
BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(context, R.style.BottomSheetDialogTheme);
bottomSheetDialog.setContentView(R.layout.layout_bottom_sheet_ayat);
ConstraintLayout constraintLayout = bottomSheetDialog.findViewById(R.id.constraintLayoutCheckBox);
ConstraintSet constraintSet = new ConstraintSet();
ConstraintLayout.LayoutParams params = new ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.MATCH_PARENT,
ConstraintLayout.LayoutParams.WRAP_CONTENT
);
int margin = (int) convertDpToPixel(30F, context);
params.setMargins(margin,0,0,0);
// for (int i = 0; i < 10; i++) {
// AppCompatCheckBox compatCheckBox = new AppCompatCheckBox(context);
// compatCheckBox.setId(i + 1);
// compatCheckBox.setText("Ayat " + i);
// compatCheckBox.setPadding(25, 0,0,0);
// constraintLayout.addView(compatCheckBox, params);
//
// constraintSet.clone(constraintLayout);
// constraintSet.connect(compatCheckBox.getId(), ConstraintSet.TOP, compatCheckBox.getId() + i, ConstraintSet.BOTTOM, 0);
// constraintSet.applyTo(constraintLayout);
// }
AppCompatCheckBox compatCheckBox = new AppCompatCheckBox(context);
compatCheckBox.setId(R.id.checkbox2);
compatCheckBox.setText("Ayat 1");
compatCheckBox.setPadding(25, 0,0,0);
constraintLayout.addView(compatCheckBox, params);
constraintSet.clone(constraintLayout);
constraintSet.connect(compatCheckBox.getId(), ConstraintSet.TOP, R.id.checkbox1, ConstraintSet.BOTTOM, 0);
constraintSet.applyTo(constraintLayout);
// constraintLayout.addView(checkBox);
bottomSheetDialog.show();
}
private float convertDpToPixel(float dp, Context context) {
return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT);
}
}
Here my layout_bottom_sheet_ayat.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/bottomSheetLinearLayout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/bottom_sheet_ayat"
android:orientation="vertical"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="15dp"
android:text="Pilih Ayat"
android:textColor="#color/black"
android:textSize="20sp"
android:textStyle="bold"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="5dp"
android:background="#color/white"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/constraintLayoutCheckBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatCheckBox
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="#+id/checkbox1"
android:layout_marginStart="15dp"
android:layout_marginTop="24dp"
android:padding="10dp"
android:checked="false"
android:enabled="true"
android:text="Semua Ayat"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginLeft="16dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
Thanks very much for any sugesstion and help :)
Finally I have simple code to create Checkbox Programmatically in BottomSheetDialog. So far I satisfied with this code.
I got clues:
Checkbox programmatically using LinearLayout from this tutorial on Youtube
and Convert dp like in layout_bottom_sheet_ayat.xml programmatically using this Stackoverflow answer
So base on those clues, I made some changes on my code below :
private class MyOnClickListener implements View.OnClickListener {
#Override
public void onClick(View v) {
int itemPosition = recyclerViewSurat.getChildLayoutPosition(v);
String jumlahAyat = suratList.get(itemPosition).getJumlahAyat();
BottomSheetDialog bottomSheetDialog = new BottomSheetDialog(context, R.style.BottomSheetDialogTheme);
bottomSheetDialog.setContentView(R.layout.layout_bottom_sheet_ayat);
LinearLayoutCompat linearLayoutCheckBox = bottomSheetDialog.findViewById(R.id.linearLayoutCheckBox);
LinearLayoutCompat.LayoutParams params = new LinearLayoutCompat.LayoutParams(
LinearLayoutCompat.LayoutParams.MATCH_PARENT,
LinearLayoutCompat.LayoutParams.WRAP_CONTENT
);
int margin = (int) convertDpToPixel(30F, context);
params.setMargins(margin, 0,0,0);
AppCompatCheckBox compatCheckBox;
int banyakAyat = Integer.parseInt(jumlahAyat);
for (int i = 1; i <= banyakAyat; i++) {
compatCheckBox = new AppCompatCheckBox(context);
compatCheckBox.setId(i);
compatCheckBox.setText("Ayat " + i);
int margin1 = (int) convertDpToPixel(10F, context);
compatCheckBox.setPadding(margin1,0,0,0);
linearLayoutCheckBox.addView(compatCheckBox, params);
}
bottomSheetDialog.show();
}
private float convertDpToPixel(float dp, Context context) {
return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT);
}
}
and my layout_bottom_sheet_ayat.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/bottomSheetLinearLayout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="550dp"
android:background="#drawable/bottom_sheet_ayat"
android:orientation="vertical"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="15dp"
android:text="Pilih Ayat"
android:textColor="#color/black"
android:textSize="20sp"
android:textStyle="bold"/>
<View
android:id="#+id/viewId"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginBottom="5dp"
android:background="#color/white"/>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="#+id/linearLayoutCheckBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatCheckBox
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="#+id/checkbox1"
android:layout_marginTop="24dp"
android:padding="10dp"
android:checked="false"
android:enabled="true"
android:text="Semua Ayat"
android:layout_marginLeft="15dp"
android:layout_marginStart="15dp" />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.core.widget.NestedScrollView>
</androidx.appcompat.widget.LinearLayoutCompat>
So thanks all :)

Android ListView isnt displaying items despite the ListView and SimpleAdapter having data

I am not sure why results arent appearing within my ListView. I even have debug logs telling me that both adapter and listview have a count of 2 items, but it still doesnt display anything. Here is the code
SelectPlayerActivity.java
PlayerDB db = new PlayerDB(this);
ArrayList<Player> data = db.getPlayers();
ArrayList<HashMap<String, String>> hashData = new ArrayList<HashMap<String, String>>();
for (Player player : data){
hashData.add(Player.ConvertPlayerToHashMap(player));
}
// create the resource, from, and to variables
int resource = R.layout.playerlist_row;
String[] from = {"name"};
int[] to = {R.id.playerName};
// create and set the adapter
SimpleAdapter adapter =
new SimpleAdapter(this, hashData, resource, from, to);
listPlayers.setAdapter(adapter);
adapter.notifyDataSetChanged();
// Both of these show the right amount of data, showing up as 2
Log.d("SelectPlayerActivity", "Adapter counted: " + adapter.getCount());
Log.d("SelectPlayerActivity", "ListView counted: " + listPlayers.getCount());
listPlayers.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
HashMap<String, String> hashPlayer = (HashMap<String, String>)listPlayers.getAdapter().getItem(position);
if (hashPlayer.getClass() != HashMap.class) {
Log.e("SelectPlayerActivity", "Error! hashPlayer is not a HashMap!");
}
else {
selectedPlayer = Player.ConvertHashMapToPlayer(hashPlayer);
finish();
}
}
});
I have also included the layout files in case its an issue with those
activity_select_player.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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"
tools:context=".SelectPlayerActivity">
<TextView
android:id="#+id/lblName"
android:layout_width="79dp"
android:layout_height="43dp"
android:layout_marginStart="12dp"
android:layout_marginLeft="12dp"
android:layout_marginTop="12dp"
android:gravity="center"
android:text="Player"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/btnApplySelection"
android:layout_width="381dp"
android:layout_height="103dp"
android:text="Apply Selection & Return to Menu"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<EditText
android:id="#+id/txtName"
android:layout_width="292dp"
android:layout_height="45dp"
android:layout_marginTop="12dp"
android:ems="10"
android:hint="Use the list below to select a player"
android:inputType="text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.333"
app:layout_constraintStart_toEndOf="#+id/lblName"
app:layout_constraintTop_toTopOf="parent" />
<ListView
android:id="#+id/listPlayers"
android:layout_width="409dp"
android:layout_height="568dp"
android:layout_marginBottom="1dp"
app:layout_constraintBottom_toTopOf="#+id/btnApplySelection"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />
</androidx.constraintlayout.widget.ConstraintLayout>
playerlist_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="wrap_content">
<TextView
android:id="#+id/playerName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"/>
</LinearLayout>
EDIT1 I have included data from the hashMap variable below, exactly as copied from the logs
hashData = {ArrayList#11212} size = 2
0 = {HashMap#11269} size = 5
"wins" -> "0"
"ties" -> "0"
"name" -> "Marky"
"id" -> "1"
"losses" -> "0"
1 = {HashMap#11270} size = 5
"wins" -> "0"
"ties" -> "0"
"name" -> "asdasd"
"id" -> "2"
"losses" -> "0"
The ListView is there, but since you let it have a fixed height of 568dp its two rows are rendered "behind" other Views, for example the app bar (in my sample app the second row was partially visible and the first row was covered by a TabLayout)
I changed the constraints for the ListView as follows to have it appear below the EditText
<ListView
android:id="#+id/listPlayers"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="1dp"
app:layout_constraintBottom_toTopOf="#+id/btnApplySelection"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/txtName"
app:layout_constraintVertical_bias="1.0" />

GridLayout adding customView as a child issue, Android

i have made a simple timetable with GridLayout and it looks like this
Now the idea is to insert required subject into specific row and column. In order to achieve that i have created a class that extends CardView in which i need to insert TextView.
code:
TimeTable.xml
<GridLayout 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"
android:columnCount="8"
android:columnOrderPreserved="true"
android:rowCount="6"
tools:context="com.digiart.schoolapp.fragments.TimetableFragment">
<android.support.v7.widget.CardView
android:id="#+id/timetable_card_space"
android:layout_column="0"
android:layout_columnSpan="1"
android:layout_columnWeight="1"
android:layout_row="0"
app:cardElevation="2dp"
app:contentPadding="5dp">
<TextView
android:id="#+id/timetable_dummy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="time"
android:textAppearance="#style/TextAppearance.AppCompat.Body1"
android:visibility="invisible" />
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:id="#+id/timetable_day1_card"
android:layout_column="1"
android:layout_columnWeight="1"
android:layout_row="0"
app:cardElevation="2dp"
app:contentPadding="5dp">
<TextView
android:id="#+id/timetable_day1_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Monday"
android:textAppearance="#style/TextAppearance.AppCompat.Body2" />
</android.support.v7.widget.CardView>
.....
........
<android.support.v7.widget.CardView
android:id="#+id/timetable_time1_card"
android:layout_column="0"
android:layout_columnWeight="1"
android:layout_row="1"
app:cardElevation="2dp"
app:contentPadding="5dp">
<TextView
android:id="#+id/timetable_time1_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="09.00"
android:textAppearance="#style/TextAppearance.AppCompat.Body2" />
</android.support.v7.widget.CardView>
.....
......
</GridLayout>
TimetableSubject:
public class TimetableSubject extends CardView {
TextView subjectText;
public TimetableSubject(Context context,int column,int row,int columnSpan,String subjectName) {
super(context);
GridLayout.LayoutParams subjectParam =new GridLayout.LayoutParams();
subjectParam.columnSpec = GridLayout.spec(column,columnSpan);
subjectParam.rowSpec = GridLayout.spec(row);
subjectText = new TextView(context);
CardView.LayoutParams textParams = new CardView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
subjectText.setText(subjectName);
subjectText.setLayoutParams(textParams);
setLayoutParams(subjectParam);
}
}
Now i get what i need to do, i need to pass row and column to the custom view, and set those as layout params. the issue i think is with the Layout parameters code, i must be messing something up there. Could anyone explain how to set layout params for this situation properly? Thanks.
This is an age old ticket and I know this is marked as solved, but I'll just leave my experience here. I faced a somewhat similar issue: I was using custom views - extended RelativeLayout - inside GridLayout, I wanted to create a Grid with two columns and N rows (rows don't matter in my case). My custom views did not want to stretch horizontally, to take up the space they have. I have tried the support library, the Android X version of the library, weights, gravity - none of these worked.
It turns out that the GridLayout's parameters were set ok. My custom view was the problem. In my case there was no need to subclass a RelativeLayout, simply subclass a LinearLayout and you can still have whatever layout built inside.
Solution:
<androidx.gridlayout.widget.GridLayout
android:id="#+id/grid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/activity_margin"
android:layout_marginEnd="#dimen/activity_margin"
app:columnCount="2">
<custom.MenuButtonGrid
android:id="#+id/item1"
app:abg_title="Title1"
app:layout_columnWeight="1"
app:layout_gravity="fill" />
<custom.MenuButtonGrid
android:id="#+id/item2"
app:abg_title="Title2"
app:layout_columnWeight="1"
app:layout_gravity="fill" />
<custom.MenuButtonGrid
android:id="#+id/item3"
app:abg_title="Title3"
app:layout_columnWeight="1"
app:layout_gravity="fill" />
</androidx.gridlayout.widget.GridLayout>
The implementation of MenuButtonGrid:
/**
* A component that creates a card view with an icon and texts.
* They are designed to use as buttons.
*/
class MenuButtonGrid #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
private var binding =
LayoutButtonGridViewBinding.inflate(LayoutInflater.from(context), this, true)
init {
// do whaterver you want in here
}
}
The inside of layout_button_grid_view.xml:
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardUseCompatPadding="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="100dp">
// Content doesn't matter here
</RelativeLayout>
</com.google.android.material.card.MaterialCardView>
You should go with TableLayout. In TableLayout you can create any number of rows and column. Go through the TableLayout documentation and read the concepts of rows and column. Here is a very nice tutorial for table layout.
This tutorial is also very helpful for you.
There was an issue with GridLayout weight parameter in Spec. Managed to solve the problem by inserting weight as a float parameter in Spec
Code:
public class TimetableSubjectView extends CardView {
TextView subjectText;
public TimetableSubjectView(Context context, int column, int row, float columnWeight, String subjectName, int color) {
super(context);
GridLayout.Spec rowI = GridLayout.spec(row,1,columnWeight);
GridLayout.Spec colI = GridLayout.spec(column,1,columnWeight);
GridLayout.LayoutParams subjectParam =new GridLayout.LayoutParams(rowI,colI);
subjectParam.setMargins(5,5,5,5);
setBackgroundColor(color);
subjectText = new TextView(context);
CardView.LayoutParams textParams = new CardView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
subjectText.setText(subjectName);
subjectText.setGravity(Gravity.CENTER);
subjectText.setLayoutParams(textParams);
this.addView(subjectText);
setLayoutParams(subjectParam);
}
}

Adding Views with Java LayoutInflater in a loop either doesn't set the width or only adds the first item in the loop

I have function that loads data form a server, like a search then adds these tot the main menu.
To accomplish this I am using a for loop on the JSON results to add the items.
This loop works fine, it reads the data and loops through fine:
Java Loop:
JSONArray teams = result.getJSONArray("teams");
LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout parent = (LinearLayout) mainMenu.findViewById(R.id.team_list_view);
//Log.d("TEAMS",teams.toString());
for(int x = 0; x < teams.length(); x++) {
JSONObject cTeam = teams.getJSONObject(x);
String name = cTeam.getString("name");
String thumb = cTeam.getString("thumb");
String id = cTeam.getString("id");
View custom = inflater.inflate(R.layout.teams_menu_template, null);
int width = LinearLayout.LayoutParams.FILL_PARENT;
int height = LinearLayout.LayoutParams.WRAP_CONTENT;
ImageButton pp = (ImageButton) custom.findViewById(R.id.tempPPbtn);
Button teamName = (Button) custom.findViewById(R.id.tempPPTxtbtn);
teamName.setText(name);
loadImage loadImage = new loadImage("imagebutton",pp);
loadImage.execute(thumb);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(width,height);
parent.addView(custom);
}
Now this does work fine it loops through and adds the image and text and appends to the parent layout. But instead of stacking the new layouts it places them side by side like in the image below:
After some googling I tried adding params to set the width to FILL_PARENT but the outcome only adds the first item. However it does add it as I want.
I've stuck on this for quite some time, if anyone can help it would be greatly appriated.
My Template XML file i'm using.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:id="#+id/tempDropCont"
android:background="#drawable/drop_down"
android:weightSum="100"
android:baselineAligned="true">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:weightSum="2">
<LinearLayout
android:orientation="vertical"
android:layout_width="70dp"
android:layout_height="wrap_content">
<ImageButton
android:layout_width="40dp"
android:layout_height="40dp"
android:id="#+id/tempPPbtn"
android:background="#drawable/profile"
android:layout_marginLeft="15dp" />
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="20dp">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/leader_board"
android:id="#+id/tempPPTxtbtn"
android:background="#android:color/transparent"
android:textColor="#color/white"
android:textSize="20sp"
android:layout_marginTop="10dp" />
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tempDrop"
android:visibility="gone">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/view_team"
android:id="#+id/tempTxtBtn1"
android:background="#android:color/transparent"
android:textColor="#color/white"
android:textSize="20sp"
android:layout_marginTop="10dp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/edit_team"
android:id="#+id/tempTxtBtn2"
android:background="#android:color/transparent"
android:textColor="#color/white"
android:textSize="20sp"
android:layout_marginTop="10dp" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/team_settings"
android:id="#+id/tempTxtBtn3"
android:background="#android:color/transparent"
android:textColor="#color/white"
android:textSize="20sp"
android:layout_marginTop="10dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
At first I did think it was the xml but I have tried using include on a different layout and it includes the file fine as its supposed too.
NOTE from the server there is two items returned.
It would have been nice to have the parent layout as well, or at least the way you defined the parent (LinearLayout with the id listview).
However, there are several culprits for the behavior that you describe:
Make sure that the parent layout has the orientation set to vertical. At this point you can copy paste a couple of template items in your layout and see if they look alright when you define them in xml
When you inflate your item, you need to pass the parent as well, so that the child inherits the layout properties:
View custom = inflater.inflate(R.layout.teams_menu_template, parent, false);
This will create the item with the expected properties as defined in the parent container, but not attach it to the parent just yet.
This line is not used:
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(width,height);
You don't set the parameters once you created them. But I think this will be redundant once you do the inflation properly.

Dynamic list of checkboxes with onclick functions

I have a page that returns a list of items backs from a database. I want to add each item to my android fragment as a checkbox dynamically with an onClick, that can tell if an item is being checked or un-checked.
How can I add checkboxes dynamically with on-clicks and different titles for each?
Below is the xml I am inserting the checkboxes into:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="#e5e5e5"
android:layout_height="match_parent" >
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:orientation="vertical"
android:background="#drawable/bg_card">
<!-- Card Contents go here -->
<TextView
android:id="#+id/styleDescription"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:ems="10"
android:textSize="15sp"
android:padding="5dip"
></TextView>
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New CheckBox"
android:id="#+id/checkBox" />
</LinearLayout >
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:orientation="horizontal"
android:background="#drawable/bg_card">
<!-- Card Contents go here -->
<Button
android:id="#+id/buttonAddList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Create List"
style="?android:attr/borderlessButtonStyle"
android:textColor="#color/orange"
android:textStyle="bold"
/>
</LinearLayout >
</FrameLayout>
</LinearLayout>
</ScrollView>
I currently have one checkbox in the above code. I plan on removing this. That checkbox is just to show where I want my check boxes to show up.
What you need to do first is add an id to your LinearLayout (in that XML file), the one which is going to hold the CheckBoxes. Then, in the code you need to get that LinearLayout by its id and use addView() to add CheckBoxes that you create dynamically. I imagine in pseudocode it'd look like this:
for (int i = 0; i < numberOfCheckBoxes; i++) {
CheckBox checkBox = new CheckBox();
checkBox.setTitle("Your title");
checkBox.setOnClickListener(new OnClickListener() {
// Your code to be executed on click
});
linearLayout.addView(checkBox);
}
Does this help?
PS: It'd be nice if you kept your code clean - ADT (and I believe Eclipse too) gives you the Shift+Ctrl+F shortcut to indent your code automatically - use it as often as possible ;)
Since you are processing database items, I suggest using a CursorAdapter to do the heavy work for you. A CursorAdapter, like any of the Adapter classes can process the database items and custom-fit them into a layout of your choice, to use in a ListView.
You have to make adjustments to your code:
Create a layout file that contains whatever you want to put in the dynamic list. This is an example, say it's named list_contents.xml:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:orientation="vertical"
android:background="#drawable/bg_card">
<!-- Card Contents go here -->
<TextView
android:id="#+id/styleDescription"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:ems="10"
android:textSize="15sp"
android:padding="5dip"
></TextView>
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New CheckBox"
android:id="#+id/checkBox" />
</LinearLayout >
</FrameLayout>
Then, instead of returning a List from your AsyncTask, return the Cursor itself
from your database. This Cursor will be processed by CursorAdapter. I recommend this guide:
http://www.gustekdev.com/2013/05/custom-cursoradapter-and-why-not-use.html
Implement the CursorAdapter methods:
In your implementation of newView(), inflate list_contents.xml (Note that if you use ResourceCursorAdapter you wouldn't need to do this)
In your implementation of CursorAdapter.bindView() do this:
CheckBox checkbox = (CheckBox) view.findViewById(R.id.checkbox);
checkbox.setText(cursor.getString(cursor.getColumnIndex(YOUR_DATABASE_COLUMN_NAME_FOR_CHECKBOX_VALUES)));
checkbox.setOnCheckedChangedListener(listenerInitializedSomewhereFromFragmentCode);
Change your ScrollView to a ListView (it can be inside any Layout), and give it an id, say R.id.listview.
Finally, in the part where you process the List from the database, where we now have a Cursor instead, just do this:
CustomCursorAdapter cca = new CustomCursorAdapter(getActivity(), resultFromDatabase, 0);
listView.setAdapter(cca);
Note: getActivity() is for when you are working inside a Fragment. It should be a context, so inside an Activity it can just be "this".
Note2: listView should have been initialized at this point via findViewById.
Note3: If listView already has an Adapter and Cursor set, you should consider calling listView.getAdapter().changeCursor() instead.
Simple Code In Kotlin
fun createCheckbox() {
var arr_cb = arrayOfNulls<CheckBox>(checkbox_size)
val layout = findViewById<View>(R.id.layout_checkbox) as ViewGroup
val ll = LinearLayout(this)
ll.orientation = LinearLayout.VERTICAL
for (i in 0 until arr_cb.size) {
arr_cb[i] = CheckBox(this)
arr_cb[i]?.text = health_status.get(i).toString()
arr_cb[i]?.setPadding(25, 0, 0, 0)
arr_cb[i]?.id = i
arr_cb[i]?.tag = health_status[i]
arr_cb[i]?.setTextColor(resources.getColor(R.color.title_color))
arr_cb[i]?.setOnCheckedChangeListener(
arr_cb[i]?.let {
handleCheck(it)
})
arr_cb[i]?.buttonTintList =
ColorStateList.valueOf(resources.getColor(R.color.theme_color))
ll.addView(arr_cb[i])
}
layout.addView(ll)
}
handleCheck method
private fun handleCheck(chk: CheckBox): CompoundButton.OnCheckedChangeListener? {
return object : CompoundButton.OnCheckedChangeListener {
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
if (!isChecked) {
//uncheck
} else {
//check
}
}
}
}
and you want to do something use direct checkboxList object like as
val layout = findViewById<View>(R.id.layout_checkbox) as ViewGroup
val ll = LinearLayout(this#MedicalHistoryActivity)
ll.orientation = LinearLayout.VERTICAL
for (i in 0 until health_status.size) {
arr_cb[i]?.isEnabled = true
// do something like change color or more
}
layout.addView(ll)
for enable checkbox or more.
Thank you

Categories