Just trying to center my RecyclerView horizontal.
Here is the xml:
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
And here the java code:
mRecyclerView = findViewById(R.id.recycler_view);
itemLayoutManager = new GridLayoutManager(this, 6);
mRecyclerView.setLayoutManager(itemLayoutManager);
...
mItemAdapter = new ItemAdapter(MainActivity.this, mItemList);
mRecyclerView.setAdapter(mItemAdapter);
When I run the application the recyclerview aligns left.
What exactly do I have to do to center the content so that the margin is the same left and right like in the example picture on the right side?
Create a class like to set paddings to item:
class SpacingItemDecorator (private val padding: Int) : RecyclerView.ItemDecoration()
{
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
)
{
super.getItemOffsets(outRect, view, parent, state)
outRect.top = padding
outRect.bottom = padding
outRect.left = padding
outRect.right = padding
}
}
Now in Your RecyclerView set paddingStart and paddingEnd, e.g 4dp :
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="4dp"
android:paddingEnd="4dp"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="6"/>
When You create adapter just apply this item decorator:
val x = (resources.displayMetrics.density * 4).toInt() //converting dp to pixels
recyclerView.addItemDecoration(SpacingItemDecorator(x)) //setting space between items in RecyclerView
RecyclerView item:
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="6dp"
app:cardElevation="4dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/txtContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{Integer.toString(dataView.id)}"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
Result:
I faced the same issue recently and looked into the source code. There's no straight forward way to do this using GridLayoutManager. The other was is to use a GridView which is more customisable.
This is an alternate solution for you or any one else looking for a workaround.
I didn't want to change to a GridView so this is what I did. I set the RecyclerView's layout width to wrap_content and centered it horizontally within the parent (which in your case is a RelativeLayout so it will be something like android:centerHorizontally="true")
I knew the item width so I calculated the spanCount by dividing the screen width by the item width (plus any horizontal margins). Now I set the span count to the grid layout manager and it gets centered horizontally just how you want it.
Adding margins to you item's layout root play an important role here.
Related
I'm writing a code for a school project where one has to load a data file (CSV file, text file, etc) and from the obtained data, the app will pass the data to a custom draw View and the onDraw method will draw/plot a graph based on the data.
My goal is for the app to display 2 graphs, one after the other (stacked). The first set of data is loaded and the 1st graph is drawn. The loaded data is then used for a different calculation in a different method. The custom draw View is then called again with the new data to draw the 2nd graph.
When I run the app, both charts are drawn but because the x and y-axis' of the graph are coded to be drawn at certain fixed pixels, the 2nd graph is drawn over the first one and therefore only the 2nd graph is visible.
Is there any way I can draw the 2 graphs so that it does not overlap and instead appears to be stacked in ScrollView?
My code is shown below but I've gotten rid of calculations that I think aren't very important. Any help and pointers would be very much appreciated!
MainActivity.java:
#Override
protected void onActivityResult(......) {
super.onActivityResult(......);
switch (1) {
case 1:
Graph graph = this.findViewById(R.id.graph1);
graph.setData(data); // the loaded data is passed to Graph View
Graph drawGraph2 = this.findViewById(R.id.graph2);
graph2.setData(this.newCalculate(data));
break;
}
}
Graph.java
public class Graph extends View {
private Paint mPaint = new Paint();
private final int zero = 700; // mark the 0 line of graph at 700 pixels
public void setData(data){
......
}
public Graph(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
}
#Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthSize, heightSize);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
plotUnit(canvas); // plot points on graph
axisLabel(canvas); // label axis
axisLine(canvas); // draw axis
xyAxisMarker(canvas); // mark axis
}
private void plotUnit(Canvas canvas) {
......
// Due to data having negative values, the graph is inverted and the 0 starts
// of the graph is defined at 700 pixels (private final int zero)
}
private void axisLabel(Canvas canvas) {
......
}
private void axisLine(Canvas canvas, int inset) {
......
}
private void xyAxisMarker(Canvas canvas) {
......
}
Update
activity_main.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">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="#+id/loadbutton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Open Data File" />
<firstapp.drawtwograph.Graph
android:id="#+id/graph1"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
<firstapp.drawtwograph.Graph
android:id="#+id/graph2"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
</LinearLayout>
</ScrollView>
</LinearLayout>
You cannot have two views' heights match parent height inside of a LinearLayout with vertical orientation. It is not possible because heights of these views must be equal to the parent height but at the same time, they must be ordered one after the other resulting in double of parent's height.
If you imagine parent's height as 10dp then each of the Graph views must be 10dp as well which means parent's height must be 20dp, not 10dp. That is going to cycle forever so the Android does a simple thing: views that are going below the first child view with android:layout_height="match_parent" will have height 0dp or if their height is fixed they will be drawn outside of the layout and will not be visible.
Example
Screenshot from Design tab of layout editor in Android Studio IDE.
Here you can see:
red view as a parent linear layout;
purple view as a first child with height matching it's parent height;
outlined view that is drawn outside of the layout because it is pushed out by the first child with android:layout_height="match_parent";
there is one more view that is crushed to 0 height and thus not visible. You can see it down in the XML code.
XML code of this sample:
<?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="100dp"
android:background="#android:color/holo_red_light">
<LinearLayout
android:background="#color/colorPrimary"
android:layout_width="60dp"
android:layout_height="match_parent" <!-- this view's height is a problem -->
android:orientation="vertical"/>
<LinearLayout
android:background="#android:color/holo_green_dark"
android:layout_width="120dp"
android:layout_height="match_parent" <!-- height is not fixed, then it will be 0 -->
android:orientation="vertical"/>
<LinearLayout
android:background="#android:color/holo_green_light"
android:layout_width="120dp"
android:layout_height="40dp" <!-- height is fixed, it is outlined outside of a layout -->
android:orientation="vertical"/>
</LinearLayout>
How to fix the issue?
Set fixed height. As a test try to define a fixed height, e.g. 100dp;
Redesign your layout. Use RelativeLayout or ConstraintLayout to position views relative to each other so that they are always visible no matter what the screen size, ratio, density is.
Example of how to fix
I personally prefer ConstraintLayout as it is very powerful in terms of positioning and adaptation.
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/loadbutton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Open Data File"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<firstapp.drawtwograph.Graph
android:id="#+id/graph1"
android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/graph2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/loadbutton" />
<firstapp.drawtwograph.Graph
android:id="#+id/graph2"
android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/graph1" />
</androidx.constraintlayout.widget.ConstraintLayout>
The result is (I used two buttons instead of Graph views):
Hints:
If you want to use ScrollView then setting fixed height or defining height at runtime will be required.
Get rid of private final int zero = 700; // mark the 0 line of graph at 700 pixels. Do not use pixel values directly as it will lead to error-prone UI. It will be the case of "work on my phone, does not work the other". Use view's height as the 0 line.
I'm trying to make chart with reclyerview. The issue that i'm having is i can't seem to make child items align from bottom, instead of the top of the reclyerview.
I have tried, reverseLayout and stackFromEnd
I have searched left and right. Still have no idea why does this happen.
Can anyone please help me.
Any helpfull ideas would be appreciated.
Layout managers align items to top-start by default because it's the most stable in case RecyclerView has height of wrap_content which could cause some layout issues.
If your RecyclerView has fixed size you can try to extend layout manager you're using to force it to lay out items on bottom:
// inside your activity or fragment where you're initializing RecyclerView
recyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) {
#Override
public void layoutDecoratedWithMargins(#NonNull View child, int left, int top, int right, int bottom) {
// calculate available space in recyclerView
int offset = getHeight() - getPaddingTop() - getPaddingBottom();
// subtract height of item being laid out
offset -= bottom - top;
// lay out item lower than usual
super.layoutDecoratedWithMargins(child, left, top + offset, right, bottom + offset);
}
});
I kinda figured it out.
As i have static height, i created another wrapper around child, that was alligned to bottom, and the parrent set as specific height.
<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="80dp"
android:layout_height="200dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginRight="8dp"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent">
<TextView
android:id="#+id/tv_time"
style="#style/BoldWhiteText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="7.5"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
I'm try to create a ListView with CardView. CardView contains always 3 rows with some info, but after that it got 2n rows that looks like:
- position, name;
- image, data, image, data.
I'm using for this task object, that contains:
- object with data, that will always fill fist 3 rows;
- list of object, that i use for 2n rows.
I've tried already:
- swapping RecyclerAdapter to ArrayAdapter (helps with visibility that I change too, but not with inflating);
- creating a method that will handle all logic related to inflating that layout
- inflating inside onBindViewHolder/getView
I will paste version with inflating CardView in another method:
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
/*inflating layout and fill it with data from first object*/
View listItem = convertView;
if(listItem == null)
listItem = LayoutInflater.from(context).inflate(R.layout.card,parent,false);
//add data
//if needed to make sure that inflating will occur once. list is
//LinearLayout inside CardView, current is entire object
if(list.getChildCount() < 1)
addList(list, current);
//setting click listeners and returning view
}
private void addList(ViewGroup parent, ListItem current){
for (Item var : ListItem.getItemList()) {
View layout = LayoutInflater.from(context).inflate(R.layout.card_part, parent, false);
//setting data
ViewGroup.LayoutParams params = layout.getLayoutParams();
params.height = LinearLayout.LayoutParams.WRAP_CONTENT;
params.width = LinearLayout.LayoutParams.WRAP_CONTENT;
layout.setLayoutParams(params);
parent.addView(layout);
}
}
#EDIT: CardView
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
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:id="#+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="#color/colorPrimary"
android:layout_margin="15dp"
app:cardCornerRadius="5dp"
app:cardElevation="25dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/id"
android:visibility="gone"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/text"
android:id="#+id/name"
android:textSize="20sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/text"
android:id="#+id/type"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="#color/text"
android:layout_weight="1"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_expand"
tools:ignore="ContentDescription"
android:id="#+id/show_list"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_hide"
tools:ignore="ContentDescription"
android:visibility="gone"
android:id="#+id/hide_list"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="#+id/list"
android:visibility="gone">
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
Actual results:
- if i comment
if(list.getChildCount() < 1)
data fill be sometimes added few time, not only from correct object.
- now with that if layout is inflating with wrong data.
Expected result:
Inflating inside CardView add data that is correct for object and connected to it list of objects.
#EDIT2:
I've tried to just create that part of View manually instead of using LayoutInflater. That does not change anything.
After some break from this topic I found way to do it. Adapter reusing old View on getView/onBindViewHolder. If Linear Layout that conatins earlier list of others elements like TextView, ImageView etc. was not cleared before adding new elements, old will stay.
The solustion is to remove old ones. On Linear Layout I needed to call removeAllViews() before adding new elements.
The issue is that the recyclerview scrolls down almost a complete row when the soft keyboard appears or disappears.
Here is a visual example:
The layout for the activity uses a MessageInput and MessageList object from the ChatKit UI library and is as follows:
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context="apps.cal.calchat.ChatActivity.ChatView">
<TextView
android:id="#+id/chatIsTypingTextView"
android:layout_width="match_parent"
android:layout_marginLeft="8dp"
android:layout_height="wrap_content"
android:layout_above="#+id/chatMessageInput"/>
<com.stfalcon.chatkit.messages.MessageInput
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:id="#+id/chatMessageInput"
android:layout_width="0dp"
android:layout_height="wrap_content" />
<com.stfalcon.chatkit.messages.MessagesList
android:id="#+id/chatMessagesList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_above="#+id/chatIsTypingTextView"
/>
</RelativeLayout>
The activity also has adjustPan set in the manifest:
<activity
android:name=".ChatActivity.ChatView"
android:windowSoftInputMode="adjustPan|stateHidden"/>
The Recyclerviews linear layout manager is set as vertical, with a reverse layout and with the default item animator:
public <MESSAGE extends IMessage>
void setAdapter(MessagesListAdapter<MESSAGE> adapter, boolean reverseLayout) {
SimpleItemAnimator itemAnimator = new DefaultItemAnimator();
itemAnimator.setSupportsChangeAnimations(false);
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext(),
LinearLayoutManager.VERTICAL, reverseLayout);
setItemAnimator(itemAnimator);
setLayoutManager(layoutManager);
adapter.setLayoutManager(layoutManager);
adapter.setStyle(messagesListStyle);
addOnScrollListener(new RecyclerScrollMoreListener(layoutManager, adapter));
super.setAdapter(adapter);
}
I ideally do not want to set up a keyboard listener and programatically scroll to the bottom every time the keyboard is shown/hidden. It seems that I must be doing something wrong somewhere. Any ideas?
When implementing the android ui chat, I spent a long time looking for a solution to the problem with scrolling messages when the keyboard opens. Basically, the decisions were based on scrolling through the recycler to the height of the keyboard and it looked like a crutch.
As it turned out, you need only few things
1. Do not use layoutManager.stackFromEnd = true
2. Do not use ConstraintLayout with high 0dp as a recycler parent
2. Do not use RelativeLayout with wrap_content height as a recycler parent
Setting example:
val adapter = MessagesAdapter ()
val layoutManager = LinearLayoutManager (this, RecyclerView.VERTICAL, true)
rvMessages.layoutManager = layoutManager
rvMessages.adapter = adapter
For layout you can use LinearLayout, RelativeLayout or ConstraintLayout with match_parent height and padding from bottom to editText height.
<LinearLayout xmlns: android = "http://schemas.android.com/apk/res/android"
android: layout_width = "match_parent"
android: layout_height = "match_parent"
android: orientation = "vertical">
<androidx.recyclerview.widget.RecyclerView
android: id = "# + id / rvMessages"
android: layout_width = "match_parent"
android: layout_height = "0dp"
android: layout_weight = "1" />
<EditText
android: id = "# + id / etMessage"
android: layout_width = "match_parent"
android: layout_height = "wrap_content" />
</LinearLayout>
I am trying to center a view within a parent RelativeLayout:
private void addLoadingAnimation() {
RelativeLayout mainView = (RelativeLayout) findViewById(R.id.medical_supply_tile_activity);
mainView.removeAllViews();
GifView gifView = new GifView(this);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
gifView.setId(R.id.loading_gif);
mainView.setBackgroundColor(getResources().getColor(android.R.color.background_light));
mainView.addView(gifView,layoutParams);
}
I have also tried adding layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 0); before the CENTER_IN_PARENT line. Nothing seems to be working. It is always aligned to the bottom right.
Here is my parent view:
<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="#android:color/background_light"
tools:context=".MedecineTileActivity"
android:id="#id/medical_supply_tile_activity"
android:layout_gravity="left">
</RelativeLayout>
The gravity=left is because when the loading animation is removed, I insert fragments and want them to go to the left.
I have achieved this with TextViews and ImageViews by aligning the child View's right to the parent's right and also aligning the child View's left to the parent's left.
<TextView
style="#style/NewTextM2"
android:id="#+id/soft_dialog_info_item_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBottom="#+id/<Parent>"
android:layout_alignLeft="#+id/<Parent>"
android:layout_alignRight="#+id/<Parent>"
android:gravity="center"
android:tag="item_text"
android:text="#string/text"
android:maxLines="1" />
This will align the view to the bottom center of the parent.