I have a custom Button and I want to set its dimensions in code (as opposed to in xml), so that users can customize the dimensions. The seemingly obvious way to this is:
public class MyButton extends Button
{
public MyButton(Context context, AttributeSet attrs)
{
super(context, attrs);
int buttonSize = getSize();
setLayoutParams(new LinearLayout.LayoutParams(buttonSize, buttonSize));
}
However, this fails to be generic because it only works if the Button's parent is a LinearLayout. Instead, I tried this:
#Override
protected void onMeasure(int specw, int spech)
{
int spec = MeasureSpec.makeMeasureSpec(getButtonSize()), MeasureSpec.EXACTLY);
super.onMeasure(spec, spec);
}
...which seems to work well. Is anyone aware of any shortcomings to this? or aware of a better way to generically set widget dimensions in code?
Doing it from onMeasure() is a good way to do it (even though your code doesn't work since you're not using the measure spec you've created.) You could also override onFinishInflate() and call getLayoutParams() and change the width and height fields.
Related
So I'm working on creating a dialog fragment to allow user to choose from some options. I have a pretty simple layout inside a constraint layout. TextView on top, recycler view, then two buttons at the bottom.
The problem is, I want the recyclerview to be wrap content, so that if there aren't a lot of options, the dialog will shrink down. However, if there are a lot of options, i'd like it to expand but then start scrolling so all views are visible on the screen.
I can't seem to get past the situation where either it constantly is large. Or if I just allow wrap content, the dialog will grow so large the bottom buttons are missing.
I'm assuming it has something to do with some particular constraint options, but I can't figure out the combination. Any ideas?
EDIT: I know an easy answer is to set a max height on the recycler view. I'm hoping to do that same thing but with constraints, so its not a fixed hard height.
EDIT2: It looks like the constraints will work nicely with wrap as default if the view model's height is fixed. I really can't deal with a fixed height view model though...
Thanks
Create a customRecyclerView that override onMeasure method.
public class CustomRecyclerView extends RecyclerView{
public CustomRecyclerView (Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomRecyclerView (Context context) {
super(context);
}
public CustomRecyclerView (Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
you can call the recyclerview like this
<com.example.yourpackage.CustomRecyclerView>
I wrote a custom text view(it doesn't matter what kind of view actually) that extends from View. I added padding to this view in my XML document and read these padding and pass it to super to apply these padding. And in my onDraw and onMeasure I also took these padding into consideration and everything works just fine.
EXCEPT, if I scroll this view via the method View.scrollTo(), the padding no longer works. By saying no longer works, I mean the content that drawn on canvas doesn't respect the view's padding, like the images shown below:
I want to know if there's any workaround on this?(PS: don't tell me to use TextView instead of making my own. I'm doing this for some reason, the only problem I want to solve here is just the padding stuff, not some brilliant alternatives, thanks!)
EDIT:
my xml
<com.example.custom.MyTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/text_area"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#drawable/text_bg"
android:padding="10dp"/>
and in my constructor:
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// by calling super, the super class will take care of the paddings
// internal, and after this, I just have to get the paddings by
// getPaddingTop(),getPaddingLeft(),getPeddingRight(),getPaddingBottom() etc.
}
and in my onMeasure:
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
......
setMeasuredDimension(getPaddingLeft()+contentWidth+getPaddingRight()
,getPaddingTop()+contentHeight+getPaddingBottom());
//whereas the contentWidth and contentHeight is determined by the widthMeasureSpec
// and heightMeasureSpec and some certain logic inside the view.
// I don't think this will cause the view's content to exceed the conten area
}
and in my onDraw:
public void onDraw(Canvas canvas){
Path path = new Path();
for(String eachLine: text){
path.moveTo(startX,startY);
path.lineTo(endX,endY);
canvas.drawTextOnPath(eachLine,path,0,0,painter);
...
}
....
//the startX,startY,endX, endY has already took the padding into consideration.
//*NOTE: The only solution that I can think of is that I control this
//drawing logic according to the padding. But this approach still won't fix the
//problem, for example: what if I scroll half line height? How do I draw the
//half of the line? So there must be some other approach that I don't know.
}
I have one imageView and I am trying to give it minus margin top as much as its height / 2. I can do it at programmatically but i wondred is it possible at xml also andorid published percentrelative layout . I don't know how to to do it or possible?
--Edit: As #aga suggests, there seems to be a way to achieve it via the Percent Support Library--
If you want to use this type of imageView more often throughout your application you could extend imageview and put your margin-code inside of it's onMeasure:
public class HalfMarginImageView extends ImageView {
public HalfMarginImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
((ViewGroup.MarginLayoutParams) getLayoutParams()).topMargin = -getMeasuredHeight() / 2;
}
}
for this to work the view must be part of a ViewGroup.Also make sure you use the constructor with AttributeSet, oltherwise you can't create the View from xml. Yo then just include a CustomView in your layout xml, select HalfMarginImageView and use it as normal imageView.
Is there any way I can get around having to add the layout_width and layout_height parameters to my custom views? I know there are a few built in android views that you don't have to supply those attributes for.
It's not a View's responsibility to decide whether or not it can/should provide these attributes. The parent ViewGroup dictates whether these attributes are mandatory or not. TableRow for instance makes them optional. Other layouts (LinearLayout, FrameLayout, etc.) require these params.
When would you want to not use the height and width parameters? I'm not sure but I think that would cause them to not even show up on the layout?
Look here for reference http://developer.android.com/guide/topics/ui/declaring-layout.html#layout-params
From the same Reference Dave has suggested.
All view groups include a width and height (layout_width and
layout_height), and each view is required to define them. Many
LayoutParams also include optional margins and borders.
So it looks like, you have to.
If reducing common and redundant attributes is what you want, then you should try styling.
Developer guide here.
The problem is that ViewGroup.LayoutParams.setBaseAttributes() uses the strict getLayoutDimension(int, String).
You need to extend whichever LayoutParams you need and override setBaseAttributes.
Inside you can either manually set width and height or use the more lenient getLayoutDimension(int, int). Finally, you'll have to override in your layout class that you are using your own LayoutParams.
#Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
public static class LayoutParams extends FrameLayout.LayoutParams {
public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
width = a.getLayoutDimension(widthAttr, WRAP_CONTENT);
height = a.getLayoutDimension(heightAttr, WRAP_CONTENT);
}
}
I'm not sure if this is possible, and I couldn't find a topic based on it, but if it's been answered before drop me a link and that will be that.
What I'm looking to do right now is resize some of the default Android widgets, specifically DatePicker and TimePicker, to use in an Activity. But as far as I can see the only result of modifying the width or height of either Picker (in a negative direction) results in a cropped view, rather than a scaled/stretched view of the widget.
I am open to my own custom widgets of my own, but I would really prefer to keep this project as simple and clean as possible, matching the Android OS UI as much as possible, so using the native DatePicker and TimePicker seems like a logical choice to me. If anyone knows how to scale these widgets down rather than cropping them, I'd really appreciate it.
Thanks.
It is a very bad hack, but it should work:
Create a new view extending LinearLayout, overwrite method getChildStaticTransformation and setStaticTransformationsEnabled explicit to true.
In the method getChildStaticTransformation you can manipulate the tranformation parameter to scale down all the content of your extended LinearLayout.
And then add the DatePicker or something else as a child of this view.
EG:
public class ZoomView
extends LinearLayout
{
private float sf = 1f;
public ZoomView(final Context context, final AttributeSet attrs)
{
super(context, attrs);
setStaticTransformationsEnabled(true);
}
public ZoomView(final Context context)
{
super(context);
setStaticTransformationsEnabled(true);
}
public void setScaling(final float sf)
{
this.sf = sf;
}
#Override
protected boolean getChildStaticTransformation(final View child, final Transformation t)
{
t.clear();
t.setTransformationType(Transformation.TYPE_MATRIX);
final Matrix m = t.getMatrix();
m.setScale(this.sf, this.sf);
return true;
}
}