So I needed a view to be always square and I wrote the following custom class
public class SquareView extends LinearLayoutCompat
{
public SquareView(Context context)
{
super(context);
}
public SquareView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public SquareView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
if (width > height)
{
setMeasuredDimension(width, width);
}
else
{
setMeasuredDimension(height, height);
}
}
}
However, the children of the view that are using match_parent and wrap_content are no longer behaving correctly
How do I fix this?
This is an answer to your larger problem, not a way to fix your custom view.
ConstraintLayout supports sizing children to have a certain aspect ratio. If you want a square LinearLayout, you can just put it inside a ConstraintLayout and enforce a 1:1 aspect ratio.
https://developer.android.com/training/constraint-layout/index.html#adjust-the-view-size
Set size as a ratio
You can set the view size to a ratio such as 16:9 if at least one of the view dimensions is set to "match constraints" (0dp). To enable the ratio, click Toggle Aspect Ratio Constraint (callout 1 in figure 10), and then enter the width:height ratio in the input that appears.
If both the width and height are set to match constraints, you can click Toggle Aspect Ratio Constraint to select which dimension is based on a ratio of the other. The view inspector indicates which is set as a ratio by connecting the corresponding edges with a solid line.
For example, if you set both sides to "match constraints", click Toggle Aspect Ratio Constraint twice to set the width be a ratio of the height. Now the entire size is dictated by the height of the view (which can be defined in any way) as shown in figure 11.
Related
I have an image and ImageView of width 45dp & height 45dp. If I use my phone this image looks good but on another phone image seems very small. If you use picture converter and put xhdpi xxhdpi... the picture is still small.
(I want to get the same experience in all screen size. Example, in pixel 2 width 45dp height 45dp looks very good, Nexus width 65dp height 65dp very good, Samsung tab3 100dp looks very good. How can I do this?
Sorry for my poor English.
In the Dimens package of your application under res folder, use separate dimen values like dimens-ldpi, dimens-hdpi, dimens-mdpi, dimens-xhdpi, dimens-xxhdpi, dimens-xxxhdpi.
Create a value in each file and use different values for them.
Or, you can visit this question. There's multiple solution mentioned with example.
You need to maintain the aspect ratio of image view by calculating the ratio of screen width and height .
Create a Java File , say ProportionalImageView :
public class ProportionalImageView extends ImageView {
public ProportionalImageView(Context context) {
super(context);
}
public ProportionalImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ProportionalImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Drawable d = getDrawable();
if (d != null) {
int w = MeasureSpec.getSize(widthMeasureSpec);
int h = w * d.getIntrinsicHeight() / d.getIntrinsicWidth();
setMeasuredDimension(w, h);
}
else super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
then use this image View in your xml file :
<com.example.ProportionalImageView
android:layout_width="matchParent"
android:layout_height="wrapContent"
android:adjustViewBounds="true"
android:scaleType="fitXY"
android:src="#mipmap/img" />
Here , replace com.example. with your package name Hope it helps
The title says it all. I need to create a square sized View which height depends entirely from size of the mobile devices width (layout_width="match_parent").
Is it possible to specify layout_height="...equals to width..." from Android's .xml file or I have to do this at runtime?
You could resize the view programmatically by extending it and using your custom class in the xml instead. For example, I will extend the ImageView class and will return a square image(by changing the height to the size of the width):
public class SquareImageView extends ImageView {
public SquareImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* Allows the view to resize
* {#inheritDoc}
*/
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(width, width); //setting height same as width here
}
}
As mentioned, using this class would always result in a square image, where the height is equal to the width.
To use it in layout file:
<com.packagename.SquareImageView
android:id="#+id/image"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
However, you have to take care of cases such as the device is in landscape, this way you'll have the width much greater than the height, so the view won't fit.
I've some buttons in my activity that inflate from a custom class that make the button exact square and fit them in parent depend on screen size and it work's fine :
public class MyButton extends Button {
public MyButton(Context context) {
super(context);
}
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int size = width > height ? height : width;
setMeasuredDimension(size, size); // make it square
}
}
and here my inflater:
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Button newGuessButton = (Button) inflater.inflate(R.layout.my_button, currentTableRow, false);
currentView.addView(newGuessButton);
and layout.xml:
<my.package.name.MyButton
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/newButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="100"/>
and my question is how can set text size in my button to change the default size if I want. I think I've to set it before add it as a view but I don't know HOW?
if you just want to set text size you can use attribute
android:textSize
or use method
Button.setTextSize()
If you want to fit text inside button you can check this answer
Thank you everyone, but I found an unofficial way:As I told, my customize class (at end) make me some button that fill the parent screen, and depend on how many of it that I want to create in a row and the width size or height size of screen(we need smaller one) we can find the size nearest number to that one to will create like this:(I want to create 5 square buttons in a row )
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
Resources resources = getApplicationContext().getResources();
scale = resources.getDisplayMetrics().density;
display.getSize(size);
int width = size.x;
int tempSize = width / 5;// (5: number of buttons)
float ff = (float) ((tempSize * 0.4) / (scale));// (0.4: for example)
....
newGuessButton.setTextSize(ff);
currentView.addView(newGuessButton);
I am using the DraggablePanel library (https://github.com/pedrovgs/DraggablePanel) to facilitate a YouTube like video player. If you are not familiar with this feature it basically allows the user to shrink the currently playing video into a small thumbnail that is docked into the corner of the screen.
Unfortunately (and I am not so sure this is an issue with the above library) I have noticed that if I apply an X and Y scale on the parent view of a VideoView, the VideoView itself will not resize its content to match.
See below screenshots, where VideoView is at its natural scale and then with its parent view scaled down to about 0.6:0.6. You should notice that in the scaled screenshot the VideoView has cropped its content instead of resizing to fit.
So I tried to force a width and height on the VideoView as its parent's scale changed. There are a few examples on the internet about overriding the dimensions of a VideoView - but here is my simple version:
public class ScalableVideoView extends VideoView {
private int mVideoWidth;
private int mVideoHeight;
public ScalableVideoView(Context context) {
super(context);
}
public ScalableVideoView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ScalableVideoView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mVideoWidth = 0;
mVideoHeight = 0;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mVideoWidth > 0 && mVideoHeight > 0) {
// If a custom dimension is specified, force it as the measured dimension
setMeasuredDimension(mVideoWidth, mVideoHeight);
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
public void changeVideoSize(int width, int height) {
mVideoWidth = width;
mVideoHeight = height;
getHolder().setFixedSize(width, height);
requestLayout();
invalidate();
}
}
The fragment that contains the VideoView is responsible for calling changeVideoSize on it when it receives a notification from the DraggablePanel library about a change in scale. At which point I calculate a new pair of width and height based on the provided scale values. (scale is from 0f to 1f)
public void setVideoViewScale(float scaleX, float scaleY) {
if (mMaxVideoWidth == 0) {
mMaxVideoWidth = mVideoView.getWidth();
}
if (mMaxVideoHeight == 0) {
mMaxVideoHeight = mVideoView.getHeight();
}
if (mMaxVideoWidth > 0) {
mVideoView.changeVideoSize((int) (scaleX * mMaxVideoWidth),
(int) (scaleY * mMaxVideoHeight));
}
}
Unfortunately this causes some really interesting results. It seems that the Video portion of the VideoView is scaling appropriately - but the bounds of the VideoView seem to be scaling too fast (causing the video to both shrink and crop).
For further demonstration here are two videos:
https://github.com/npike/so_scalevideoview/blob/master/demo_videos/so_videoview_noscale.mp4
https://github.com/npike/so_scalevideoview/blob/master/demo_videos/so_videoview_scale.mp4
I have also uploaded this sample project to GitHub so that you can see the complete code:
https://github.com/npike/so_scalevideoview
I was having the same problem with my app, so I took your example that is much simpler, and tried to fix it.
This is the code that fixes the problem. Edit your changeVideoSize(int width, int height) method to:
public void changeVideoSize(int width, int height){
mVideoWidth = width;
mVideoHeight = height;
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(width, height);
lp.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
lp.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
setLayoutParams(lp);
requestLayout();
invalidate();
}
With the invalidation of the view you force the VideoView to redraw the whole View with the new layout parameters.
I believe VideoView does not support scaling. Within the sample app that comes with DraggablePanel, there is a VideoSampleActivity, which demonstrates a draggable video. In order to achieve an effect like scaling, the top_view_resize attribute is set to true within activity_video_sample.xml:
<com.github.pedrovgs.DraggableView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:draggable_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/draggable_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
draggable_view:top_view_id="#+id/video_view"
draggable_view:bottom_view_id="#+id/iv_thumbnail"
draggable_view:top_view_x_scale_factor="#dimen/x_scale_factor"
draggable_view:top_view_y_scale_factor="#dimen/y_scale_factor"
draggable_view:top_view_height="#dimen/top_fragment_height"
draggable_view:top_view_margin_right="#dimen/top_fragment_margin"
draggable_view:top_view_margin_bottom="#dimen/top_fragment_margin"
draggable_view:enable_minimized_horizontal_alpha_effect="false"
draggable_view:top_view_resize="true"
android:background="#color/black">
Setting top_view_resize to true causes the TransformerFactory to return a ResizeTransformer instead of a ScaleTransformer. The ResizeTransformer uses setLayoutParams() to resize the associated View, where the ScaleTransformer applies a scale factor.
Rather than attempting to scale the VideoView, you should be able to set top_view_resize to true.
Note: if you use a DraggablePanel (which supports top and bottom fragments) instead of a DraggableView, you currently do not have access to the top_view_resize attribute of the DraggableView that's nested inside the DraggablePanel. I consider this to be a bug, and I'm hoping I can work with Pedro to expose a top_view_resize attribute on DraggablePanel that will pass its value through to the nested DraggableView. As a result of this bug, DraggablePanel always uses the default Transformer, which is the ScaleTransformer -- which does not work with a VideoView.
I'm using the HorizontalListView provided here, and I'm trying to show custom views with a fixed height and width, like this:
public class MyView extends View {
public MyView(Context context) {
super(context);
init();
}
private void init() {
setBackgroundColor((int) (Math.random() * Integer.MAX_VALUE));
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
int h = getHeight(); // shows 255, correct height
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int w = MeasureSpec.makeMeasureSpec(425, MeasureSpec.EXACTLY);
int h = MeasureSpec.makeMeasureSpec(255, MeasureSpec.EXACTLY);
super.onMeasure(w, h);
}
}
When showing this MyView in a normal LinearLayout, the height and width are perfect. However, when I show the view in the HorizontalListView, the width is perfect, but the height is not. See this screenshot:
The width is 425px, which is correct, the height is only 160px instead of 255.
In the source of the HorizontalListView there is this method:
private void addAndMeasureChild(final View child, int viewPos) {
LayoutParams params = child.getLayoutParams();
if(params == null) {
params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
}
addViewInLayout(child, viewPos, params, true);
child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
}
Is there something I should change in this method, or is there something else to make this work?
When you do int h = MeasureSpec.makeMeasureSpec(255, MeasureSpec.EXACTLY);, that 255 value is a PX value. If you said the height was 200 DP in the XML that value will be adjusted according to the device. Not sure if the screen shown is HDPI or XHDPI (my guess is the latter), it will be multiplied, so your 200 DP becomes 300 PX in an HDPI phone (x1.5). You can either use a PX value in the XML or you can define a dimension DP value that's 200DP in a values.xml.
The preference would be the latter. You can use that dimension value in your XML and retrieve that from the Resources when you create your View. That means that if at some point you want to make it bigger you can just change one value and you're done.
Edit
I see you found the real problem. In any case, you should still use the dimension approach, it will make your code cleaner and it will work in any phone (255 will only look right in a specific combination).