RecyclerView implement GridView - android

Problem
I want to use RecyclerView to implement GridView.In fact I need to display all local photos in RecyclerView which has 3 columns.I know about GridLayoutManager
My code :
mManager = new GridLayoutManager(this,3);
And the item XML:
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
however how can I set the item view (ImageView)'s width and height?
Solution
I define a custom view:
public class RatioImageView extends ImageView {
private int originalWidth;
private int originalHeight;
public RatioImageView(Context context) {
super(context);
}
public RatioImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RatioImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setOriginalSize(int originalWidth, int originalHeight) {
this.originalWidth = originalWidth;
this.originalHeight = originalHeight;
}
#Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (originalWidth > 0 && originalHeight > 0) {
float ratio = (float) originalWidth / (float) originalHeight;
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (width > 0) {
height = (int) ((float) width / ratio);
}
setMeasuredDimension(width, height);
}
else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
Is there another solution?

Instead of creating Custom ImageView when you are creating your view inside onCreateViewHolder() method by inflating your recycler item view, you can set width and height to your ImageView using layoutParamas.
If you want 3 columns then you can get screen width and divide it into 3 and pass that to your RecyclerView Adapter and use it in onCreateViewHolder() to apply.

Related

I want to have an imageView be a perfect square regardless of the height

I have an imageView in a row layout which I use for a recyclerView. The height of this imageView is set to match_parent, but for it to be a perfect square I can't hard-code the width of the imageView.
What I need is a way to set the width of the imageView to be exactly the same as the height of the imageView when it is displayed, since setting the width to wrap_content or match_parent throws the rest of the layout off since it is rectangular, sizeable images.
Any help would be appreciated.
You can do something like this:
public class FixedAspectRatioFrameLayout extends FrameLayout {
private float ratio;
public FixedAspectRatioFrameLayout(#NonNull Context context) {
super(context);
}
public FixedAspectRatioFrameLayout(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attributeSet) {
fillFromAttrs(context, attributeSet);
}
private void fillFromAttrs(Context context, AttributeSet attributeSet) {
TypedArray array = context.obtainStyledAttributes(attributeSet, R.styleable.FixedAspectRatioFrameLayout);
ratio = array.getFloat(R.styleable.FixedAspectRatioFrameLayout_ratio, 0);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int originalWidth = MeasureSpec.getSize(widthMeasureSpec);
int originalHeight = MeasureSpec.getSize(heightMeasureSpec);
int finalWidth = originalWidth;
int finalHeight = originalHeight;
if (ratio != 0) {
if (originalHeight == 0) {
finalHeight = (int) (originalWidth / ratio);
} else if (originalWidth == 0) {
finalWidth = (int) (originalHeight * ratio);
}
}
super.onMeasure(
MeasureSpec.makeMeasureSpec(finalWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(finalHeight, MeasureSpec.EXACTLY)
);
}
}
You also need to specify attribute "ratio" in your res/values/attrs.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="FixedAspectRatioFrameLayout">
<attr name="ratio" format="float"/>
</declare-styleable>
</resources>
So now you can specify, for example, height of this FrameLayout as you want, set width to 0dp and ratio to 1 and put your ImageView inside this FrameLayout
Also, in ConstraintLayout if you set height to match constraints, you can specify ratio for this view:

Expandable height of CardView

I am working on expand and collapse of properties on CardView.
public class SimpleCardView extends CardView {
private int animationDuration;
public SimpleCardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public SimpleCardView(Context context) {
super(context);
}
public SimpleCardView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void expand(){
final int initialHeight = getHeight();
final float scale = getContext().getResources().getDisplayMetrics().density;
int targetHeight = (int) (232 * scale + 0.5f);
final int distance_to_expand = targetHeight - initialHeight;
Animation animation = new Animation() {
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
getLayoutParams().height = (int) (initialHeight +(distance_to_expand*interpolatedTime));
requestLayout();
}
#Override
public boolean willChangeBounds() {
return true;
}
};
animationDuration = distance_to_expand;
animation.setDuration((long)distance_to_expand);
startAnimation(animation);
}
public int getAnimationTime(){
return animationDuration;
}
public void collapse(){}
}
This is my screenshot:
I am setting the constant value for target height.
int targetHeight = (int) (232 * scale + 0.5f);
Here, targetHeight is a expandable height of CardView.
Due to this, when the content is too long, only few portion of content is display.
Is there any way to set that height dynamically not a constant value?
Have you Tried with :wrap_content
android:layout_height="wrap_content"
<android.support.v7.widget.CardView
android:id="#+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content">
And you need to remove static height form class SimpleCardView.
**OR You need to calculate Height **
For this you need to set Text into TextView and then
calculate height of TextView With Below lines and then pass into SimpleCardView by making any setter method .
TextView textview ;
textveiw.setText("your text");
textview.mesure(0,0);
int height = textview.getMesuredHeight();

Drawing a square layout inside a circle

I am trying to make a relative layout bounded within a circle i.e the relative layout should be like the square shown in the figure below.
I am trying to set width and height of the layout as:
√((diameter)²/2) which is about 70 %
(source: yogaflavoredlife.com)
public class SquareLayout extends RelativeLayout {
public SquareLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int originalWidth = MeasureSpec.getSize(widthMeasureSpec);
int originalHeight = MeasureSpec.getSize(heightMeasureSpec);
int required = Math.min(originalWidth, originalHeight) * 7 / 10;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(required, required);
}
}
What I am getting is a rectangular layout instead of square layout:
Can anyone guide me where I am going wrong?
Sample usage:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.widget.SquareLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F55C5C">
</com.example.widget.SquareLayout>
</RelativeLayout>
Here's how I got the solution. First I created a square frame to hold all the layouts.
public class SquareFrame extends FrameLayout {
public SquareFrame(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int originalWidth = MeasureSpec.getSize(widthMeasureSpec);
int originalHeight = MeasureSpec.getSize(heightMeasureSpec);
int required = Math.min(originalWidth, originalHeight);
super.onMeasure(
MeasureSpec.makeMeasureSpec(required, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(required, MeasureSpec.EXACTLY));
}
}
Then inserted all layouts within that square frame.
<com.example.widget.SquareFrame
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#5CF5FC">
<com.example.widget.SquareLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F55C5C">
</com.example.widget.SquareLayout>
</com.example.widget.SquareFrame>
Here is what I got
a square, not a rectangle.

Changing number of columns in RecyclerView gridlayout

I am trying to change the number of columns that appear in the recycler view (grid layout) based on the display size. However I couldn't figure out a proper way of achieving it. At the moment I am using treeViewObserver to change the number of columns based the change in screen size (during orientation). So if the app opens in portrait mode, number of columns (on the phone) it decides to be one, which look good but this method doesn't work when the app directly opens in landscape mode where a single streched out card in the grid is displayed on the screen.
Here recList is RecyclerView & glm is GridLayoutManager used in RecyclerView
viewWidth = recList.getMeasuredWidth();
cardViewWidthZZ = recList.getChildAt(0).getMeasuredWidth();
if (oldWidth == 0) {
oldWidth = cardViewWidthZZ;
}
if (oldWidth <= 0)
return;
int newSpanCount = (int) Math.floor(viewWidth / (oldWidth / 1.3f));
if (newSpanCount <= 0)
newSpanCount = 1;
glm.setSpanCount(newSpanCount);
glm.requestLayout();
Best Regards
public class VarColumnGridLayoutManager extends GridLayoutManager {
private int minItemWidth;
public VarColumnGridLayoutManager(Context context, int minItemWidth) {
super(context, 1);
this.minItemWidth = minItemWidth;
}
#Override
public void onLayoutChildren(RecyclerView.Recycler recycler,
RecyclerView.State state) {
updateSpanCount();
super.onLayoutChildren(recycler, state);
}
private void updateSpanCount() {
int spanCount = getWidth() / minItemWidth;
if (spanCount < 1) {
spanCount = 1;
}
this.setSpanCount(spanCount);
}}
If you provide a fixed column width, you can extend RecyclerView and set the span count in onMeasure accordingly:
public AutofitRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
if (attrs != null) {
// Read android:columnWidth from xml
int[] attrsArray = {
android.R.attr.columnWidth
};
TypedArray array = context.obtainStyledAttributes(attrs, attrsArray);
columnWidth = array.getDimensionPixelSize(0, -1);
array.recycle();
}
manager = new GridLayoutManager(getContext(), 1);
setLayoutManager(manager);
}
protected void onMeasure(int widthSpec, int heightSpec) {
super.onMeasure(widthSpec, heightSpec);
if (columnWidth > 0) {
int spanCount = Math.max(1, getMeasuredWidth() / columnWidth);
manager.setSpanCount(spanCount);
}
}
See my blog post for more info:
http://blog.sqisland.com/2014/12/recyclerview-autofit-grid.html
Here's my solution.
public class ResponsiveGridLayoutManager extends GridLayoutManager {
boolean hasSetupColumns;
int columnWidthPx;
public ResponsiveGridLayoutManager(Context context, int orientation,
boolean reverseLayout, int columnWidthUnit, float columnWidth) {
super(context, 1, orientation, reverseLayout);
Resources r;
if (context == null) {
r = Resources.getSystem();
} else {
r = context.getResources();
}
float colWidthPx = TypedValue.applyDimension(columnWidthUnit,
columnWidth, r.getDisplayMetrics());
this.columnWidthPx = Math.round(colWidthPx);
}
public ResponsiveGridLayoutManager(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
int[] requestedValues = {
android.R.attr.columnWidth,
};
TypedArray a = context.obtainStyledAttributes(attrs, requestedValues);
this.columnWidthPx = a.getDimensionPixelSize(0, -1);
a.recycle();
}
#Override
public int getWidth() {
int width = super.getWidth();
if (!hasSetupColumns && width > 0) {
this.setSpanCount((int)Math.floor(width / this.columnWidthPx));
}
return width;
}
}
You can use it with either XML:
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="com.caff.localflix.ResponsiveGridLayoutManager"
android:columnWidth="148dp" />
Or Java:
ResponsiveGridLayoutManager layoutManager = new ResponsiveGridLayoutManager(
this,
GridLayoutManager.VERTICAL,
false,
TypedValue.COMPLEX_UNIT_DIP,
148f
);
You could create a screen dependent resource file and load it. A booleans.xml in values-w820p folder for example. Or possibly just create a layout for large screens and be done with it.

Can't properly measure custom view?

I'm writing a custom View object, but I can't seem to get it to measure correctly. By looking at the View source code, I thought calling setMinimumHeight() and setMinimumWidth() would be enough (that's really all I need, a minimum size that the parent layout should respect). Here's my code:
public class MonthView extends View {
private final int minCellSize = 24;
public MonthView(Context context) {
super(context);
}
public MonthView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MonthView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final float scale = getContext().getResources().getDisplayMetrics().density;
setMinimumHeight((int) (minCellSize * scale * 6));
setMinimumWidth((int) (minCellSize * scale * 7));
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.RED);
}
}
Pretty simple. I then embed it in a LinearLayout, something like this:
<?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="fill_parent"
>
<com.foghina.adtp.MonthView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:text="I am below the monthview!"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
However, the MonthView takes up the entire screen and the TextView is not visible. How can I correctly write my View so that it has a minimum height / width when wrap_content is used?
I figured it out eventually. I had to write my own version of View.getDefaultSize() that is just slightly different. Here's how I used it:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final float scale = getContext().getResources().getDisplayMetrics().density;
setMeasuredDimension(getSize((int) (minCellSize * scale * 7), widthMeasureSpec),
getSize((int) (minCellSize * scale * 6), heightMeasureSpec));
}
private static int getSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
result = size < specSize ? size : specSize;
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
Pretty nasty. I don't understand why the default View.getDefaultSize() doesn't work like that.
I think you must use setMeasureDimension()

Categories

Resources