I have a small EditText and I want to display errors (using editText.setError()) in it. In Android API 10 the message is displayed in a lot of lines and it's unreadable. In Android 15 works relatively fine. I attach screenshots to illustrate the problem at the end of the question.
How I can display the error messages in a appropriate mode?
I wrote a little example to reproduce the problem:
The Activity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
((EditText) findViewById(R.id.b)).setError("A error description and bla bla bla bla bla.");
}
The layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<EditText
android:id="#+id/a"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1" />
<EditText
android:id="#+id/b"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1" />
<EditText
android:id="#+id/c"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1" />
<EditText
android:id="#+id/d"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1" />
<EditText
android:id="#+id/e"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1" />
<EditText
android:id="#+id/f"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout>
Device with Android API 10:
Tablet with Android API 15:
Related question. But the answer doesn't work for me.
UPDATE
I executed the same code on two equals simulators except the API level. The results can be seen on the screens. The API 15 still does not fix the error completely. The text is legible but the popup is not in the correct position.
So looking at the source for 2.3.3 the width of the error text is set to slightly less than the width of the TextView it is related to.
They've jimmied around with that for 4.0.3 so that, in your example, the width of the pop-up is correct - but the nature of your layout is such that the pointer is in the wrong place.
I think you've a reasonable example for a bug report against 4.0.3 as I don't think you've got that unusual a use-case.
In order to sort this out though I'd recommend using a TextView that you hide and reveal as necessary. You can set an error image on the edit text as below.
Drawable errorImage = getContext().getResources().getDrawable( R.drawable.your_error_image);
theEditTextInQuestion.setCompoundDrawableWithIntrinsicBounds(errorImage, null, null, null);”
on api 14, TextView use this class;
private static class ErrorPopup extends PopupWindow {
private boolean mAbove = false;
private final TextView mView;
private int mPopupInlineErrorBackgroundId = 0;
private int mPopupInlineErrorAboveBackgroundId = 0;
ErrorPopup(TextView v, int width, int height) {
super(v, width, height);
mView = v;
// Make sure the TextView has a background set as it will be used the first time it is
// shown and positionned. Initialized with below background, which should have
// dimensions identical to the above version for this to work (and is more likely).
mPopupInlineErrorBackgroundId = getResourceId(mPopupInlineErrorBackgroundId,
com.android.internal.R.styleable.Theme_errorMessageBackground);
mView.setBackgroundResource(mPopupInlineErrorBackgroundId);
}
void fixDirection(boolean above) {
mAbove = above;
if (above) {
mPopupInlineErrorAboveBackgroundId =
getResourceId(mPopupInlineErrorAboveBackgroundId,
com.android.internal.R.styleable.Theme_errorMessageAboveBackground);
} else {
mPopupInlineErrorBackgroundId = getResourceId(mPopupInlineErrorBackgroundId,
com.android.internal.R.styleable.Theme_errorMessageBackground);
}
mView.setBackgroundResource(above ? mPopupInlineErrorAboveBackgroundId :
mPopupInlineErrorBackgroundId);
}
private int getResourceId(int currentId, int index) {
if (currentId == 0) {
TypedArray styledAttributes = mView.getContext().obtainStyledAttributes(
R.styleable.Theme);
currentId = styledAttributes.getResourceId(index, 0);
styledAttributes.recycle();
}
return currentId;
}
#Override
public void update(int x, int y, int w, int h, boolean force) {
super.update(x, y, w, h, force);
boolean above = isAboveAnchor();
if (above != mAbove) {
fixDirection(above);
}
}
}
And call like this;
final float scale = getResources().getDisplayMetrics().density;
mPopup = new ErrorPopup(err, (int) (200 * scale + 0.5f), (int) (50 * scale + 0.5f));
mPopup.setFocusable(false);
So Android TextView use your phone density. I tried for change density but i couldn't work because it need root. If you can this, maybe its work. But i guess it is not possible.
I think you should set the width of the edit text as fill parents so that it will take the proper place, otherwise give the width like 200 or 250 dp so that in that particular width your error message will be shown to you.
does those two emulator have the same screen sizes?.. if so, i think it would be appropriate to set the layout_widths and the heights to wrap content for the popup and if the error still persist then define specifically the width and height of the popup
I think a problem might be that the LinearLayout in your XML has no weightSum attribute. I do not know if you can use layout_weight in child views if LinearLayout does not have a weightSum declared. Also, try changing your layout_widths to "wrap_content" and get rid of the layout_weights if that doesnt work.
Related
I have a Spinner in my app, with customized dropdown views. This is what the layout of the dropdown items look like:
<LinearLayout 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="wrap_content"
android:focusable="false"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatImageButton
android:id="#+id/leadingButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_weight="0" />
<FrameLayout
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_gravity="center_vertical"
android:layout_weight="1">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/dropdown_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/dropdown_text_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#id/dropdown_text" />
</RelativeLayout>
</FrameLayout>
<androidx.appcompat.widget.AppCompatImageButton
android:id="#+id/trailingButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_weight="0" />
</LinearLayout>
Android Studio warns me that my FrameLayout is useless. But when I take out the FrameLayout the dropdown views become narrow, and don't align with the spinner itself anymore. I have had the same problem when I tried to rewrite the drop-down items with a ConstraintLayout: the dropdown list became narrow, about half of the Spinner's size, and could not display all text, even though the ConstraintLayout had android:layout_width="match_parent".
A sketch to illustrate what I mean:
Why does this happen? How can I predict what the width of the dropdown menu will be based on the layout?
I find this dropdown view sizing quite magical
Did you look at the source code of the Spinner class? I just did. Here's what I found (API 27 Sources):
The spinner uses a ListView internally (first LOL), backed by DropdownPopup (private class):
private class DropdownPopup extends ListPopupWindow implements SpinnerPopup {
Before looking at it, look at ListPopupWindow because has a lot of info about the problems it has to deal with. It's a big class but among these things, you can see:
private int mDropDownHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
private int mDropDownWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
private int mDropDownHorizontalOffset;
private int mDropDownVerticalOffset;
It appears the DropDown is -by default- WRAPPING the content based upon the base class, however, the DropDownPopup that drives (and contains the adapter with all the items in the spinner) also has a void computeContentWidth() { method.
This method is called from the show() method, so before showing the popup, this computation happens every time.
I think here's part of the answer you're looking for:
void computeContentWidth() {
final Drawable background = getBackground();
int hOffset = 0;
if (background != null) {
background.getPadding(mTempRect);
hOffset = isLayoutRtl() ? mTempRect.right : -mTempRect.left;
} else {
mTempRect.left = mTempRect.right = 0;
}
final int spinnerPaddingLeft = Spinner.this.getPaddingLeft();
final int spinnerPaddingRight = Spinner.this.getPaddingRight();
final int spinnerWidth = Spinner.this.getWidth();
if (mDropDownWidth == WRAP_CONTENT) {
int contentWidth = measureContentWidth(
(SpinnerAdapter) mAdapter, getBackground());
final int contentWidthLimit = mContext.getResources()
.getDisplayMetrics().widthPixels - mTempRect.left - mTempRect.right;
if (contentWidth > contentWidthLimit) {
contentWidth = contentWidthLimit;
}
setContentWidth(Math.max(
contentWidth, spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight));
} else if (mDropDownWidth == MATCH_PARENT) {
setContentWidth(spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight);
} else {
setContentWidth(mDropDownWidth);
}
if (isLayoutRtl()) {
hOffset += spinnerWidth - spinnerPaddingRight - getWidth();
} else {
hOffset += spinnerPaddingLeft;
}
setHorizontalOffset(hOffset);
}
You may want to DEBUG and set breakpoints here to observe what these values are and what they mean.
The other piece there is the setContentWidth() method. This method is from the ListPopupWindow, and looks like:
/**
* Sets the width of the popup window by the size of its content. The final width may be
* larger to accommodate styled window dressing.
*
* #param width Desired width of content in pixels.
*/
public void setContentWidth(int width) {
Drawable popupBackground = mPopup.getBackground();
if (popupBackground != null) {
popupBackground.getPadding(mTempRect);
mDropDownWidth = mTempRect.left + mTempRect.right + width;
} else {
setWidth(width);
}
}
And setWidth (also in that class) all it does is:
/**
* Sets the width of the popup window in pixels. Can also be {#link #MATCH_PARENT}
* or {#link #WRAP_CONTENT}.
*
* #param width Width of the popup window.
*/
public void setWidth(int width) {
mDropDownWidth = width;
}
This mDropDownWidth seems used all over the place, but also made me found this other method in ListPopupWindow...
/**
* Sets the width of the popup window by the size of its content. The final width may be
* larger to accommodate styled window dressing.
*
* #param width Desired width of content in pixels.
*/
public void setContentWidth(int width) {
Drawable popupBackground = mPopup.getBackground();
if (popupBackground != null) {
popupBackground.getPadding(mTempRect);
mDropDownWidth = mTempRect.left + mTempRect.right + width;
} else {
setWidth(width);
}
}
So there you have it, more logic needed including the "window dressing" (?)
I agree the Spinner is a badly designed class (or rather, with outdated design) and even more so with the name (at this Google I/O in 2019, they actually explained in one of the sessions why the name "Spinner" hint: it comes from the 1st android prototypes). By looking at all this code, it would take a few hours to figure out what the spinner is trying to do and how it works, but the trip won't be pleasant.
Good luck.
I will reiterate my advice to use ConstraintLayout which you said you were familiar with; at the very least, discard weights.
By looking at how this works (ListView!!!) the weight calculation warrants a 2nd measure/layout pass, which is not only extremely inefficient and not needed, but also may be causing issues with the internal data adapter this DropDown thing manages so the "list" is displayed.
Ultimately, another class is also involved, this is all presented in a PopupView. PopupViews are what you see when you open a Menu item for example, and are very hard to customize sometimes, depending what you want to do.
Why Google chose this approach at the time, I don't know, but it certainly warrants an update and Material Design hasn't brought much to the table in this regard yet, as it will always be incomplete or in alpha state a year behind anything else.
It is telling you the FrameLayout is useless because it has a single child view ( the Relative Layout).
Your Framelayout has width defined as so:
<FrameLayout
android:layout_width="0dp"
android:layout_weight="1"
Your relative layout has its width defined as:
<RelativeLayout
android:layout_width="match_parent"
So just removing the FrameLayout means that a different "rule" is in place for the width.
To truly replace the FrameLayout with the RelativeLayout it should look like this:
<RelativeLayout
android:layout_width="0dp"
android:layout_weight="1"
I have such layout (I've removed some attributes, cause they really doesn't matter, full demo project is here):
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content" (* this is important)
android:layout_height="wrap_content"
android:breakStrategy="balanced" (* this is important)
android:text="#string/long_text" />
</LinearLayout>
The text long_text is quite long, so it would be separated for a few lines.
Two important lines are android:layout_width="wrap_content" and android:breakStrategy="balanced".
I expect that TextView will calculate width according to the actual text width, but it doesn't.
Does anybody know how to fix this?
UPDATE
Attribute android:breakStrategy="balanced" works only for API 23 and higher. In my example text takes 3 lines, and balanced strategy makes each line approximately same width.
I expect that in this case width of view itself will be the same as the longest line.
I'm looking for any workaround. I need solution like in Hangouts chat. I can't figure out how it works.
UPDATE 2
Unfortunately, accepted answer doesn't work with RTL text.
This happens because when TextView calculates its width it measure all text as 1 line (in your case) and on this step it knows nothing about android:breakStrategy. So it is trying to utilize all available space to render as much text on first line as it can.
To get more details, please check method int desired(Layout layout) here
To fix it you can use this class:
public class MyTextView extends TextView {
public MyTextView(Context context) {
super(context);
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Layout layout = getLayout();
if (layout != null) {
int width = (int) Math.ceil(getMaxLineWidth(layout))
+ getCompoundPaddingLeft() + getCompoundPaddingRight();
int height = getMeasuredHeight();
setMeasuredDimension(width, height);
}
}
private float getMaxLineWidth(Layout layout) {
float max_width = 0.0f;
int lines = layout.getLineCount();
for (int i = 0; i < lines; i++) {
if (layout.getLineWidth(i) > max_width) {
max_width = layout.getLineWidth(i);
}
}
return max_width;
}
}
which I took here - Why is wrap content in multiple line TextView filling parent?
You'll need to measure the width of the text in TextView programatically, like so:
Rect bounds = new Rect();
textView.getPaint().getTextBounds(textView.getText().toString(), 0, textView.getText().length(), bounds);
Now, you can place a colored rectangle behind the TextView, and set its width programatically after measuring the text (I'm using FrameLayout to put the Views one on top of the other, but you can use RelativeLayout as well):
XML:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="+#id/background"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#color/green" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:breakStrategy="balanced"
android:text="#string/long_text" />
</FrameLayout>
Code:
Rect bounds = new Rect();
textView.getPaint().getTextBounds(textView.getText().toString(), 0, textView.getText().length(), bounds);
View bg = findViewById(R.id.background);
gb.getLayoutParams().width = bounds.width();
Code is untested, but I'm sure you get the point.
UPDATE
It may be possible to do this without using a second background view, by setting the TextView width to match bounds.width(), but this trigger a change in how the text breaks, so need to be careful not to cause an infinite loop.
My activity shall have a screen divided into two sections. The first will hold custom view that occupies as much space as possible. The second section holds matrix of buttons. I do not their number at design time, I want to create them in Activity based on screen size and other conditions (easy level will have less buttons).
I currently use LinearLayout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:id="#+id/puzzleScreen"
android:layout_width="match_parent" android:layout_height="match_parent">
<lelisoft.com.lelimath.view.TilesView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tiles" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/choices">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="6"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="23"/>
</LinearLayout>
</LinearLayout>
TilesView had consumed complete screen until I overriden onMeasure(). To continue I have to calculate available space, number of buttons and divide space between both sections and pass this information to TilesView and LayoutManagers. Unfortunatelly onCreate() is not correct location for this calculation as I cannot figure out dimensions of root view (getters return 0).
Where is the correct place in Activity lifecycle where to get dimensions and create all buttons dynamically? It seems to me like chicken-egg problem.
UPDATE
I tried to debug other methods: onStart() and onResume() return 0, then measurement is called and finally onCreateOptionsMenu() receives coordinates. Too late.
UPDATE 2
The question is how to find dimensions of the contentView from Activity before measurement. I can get getWindowManager().getDefaultDisplay(), but it contains action bar etc.
protected void onCreate(Bundle state) {
super.onCreate(state);
setContentView(R.layout.activity_puzzle);
View puzzleView = findViewById(R.id.puzzleScreen);
// todo get maximum width and height for R.layout.activity_puzzle
Do you know exactly how many rows the bottom matrix has?
You could set the android:layout_weight="1" and android:layout_height="0px"
to tell the ViewGroup that your customView will occupy all the remaining space excluding the Button Matrix.
For your first question:
In onCreate() lifeCycle ,you can not get the dimensions.
Because your the all view have not measured()!
I always use view.post() to get the dimension() in onCreate() method;
view.post(new Runnable() {
#Override
public void run() {
view.getMeasuredHeight();
}
});
For your second question:
On the bottom section,I think you should use custom view extends ViewGroup.Override constructor、onMeasure()、onLayout():
public class TextBlocksView extends ViewGroup {}
In constructor, you add LinearLayout(orientation:horizontal) as much as Line Number,and add each item(addView() method and layoutParams weight 1!!) ....
public TextBlocksView(Context context, AttributeSet attrs) {
super(context, attrs);
this.removeAllViews();
.....
this.addView(linearLayoutView);
}
In onMeasure(), you calculate your dimensions in this place.(Tips:pay attention to dipToPx method!)
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
View childAt = this.getChildAt(0);
int measuredHeight = childAt.getMeasuredHeight();
int measuredWidth = childAt.getMeasuredWidth();
childAt.layout(left, top, right, bottom);
.......
}
in onLayout(),you place your Line item(LinearLayout orientation:horizontal)!
On the top section. you can use the same way!
My mother tongue is chinese,i am not good at english. Hope some guys edit my answer correctly! Thanks a lot.
I have a very simple RelativeLayout subclass that adds an image view with a text view on top of it. I have a method, show(), which creates and adds the child views and sets the initial text.
At the point I call show() for the first time, the view does not know how big it is, so I can't set the textSize nor the padding for the textView.
I have a solution that mostly works, where I call setTextSize() and setPadding() for the textView within the overridden method, onSizeChanged(). The text does not show the first time it is displayed. However, it shows every time after that, perfectly sized and placed.
Here is the code for onSizeChanged():
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
Log.e(TAG, "onSizeChanged() called");
if (_childTextView != null) {
float textSize = h / 2.0f;
int topPadding = (int)(h / 3.0f);
Log.e(TAG, "setting textSize = " + textSize);
Log.e(TAG, "topPadding = " + topPadding);
_childTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
_childTextView.setPadding(0, topPadding, 0, 0);
}
super.onSizeChanged(w, h, oldw, oldh);
Log.e(TAG, "end onSizeChanged()");
}
The code for show() is as follows:
public void show(int val) {
_val = val;
Log.e(TAG, "in show(), val = " + val);
// create and add background image if not already there
if (_backgroundImageView == null) {
_backgroundImageView = new ImageView(_context);
_backgroundImageView.setImageResource(R.drawable.background);
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
params.addRule(CENTER_IN_PARENT);
addView(_backgroundImageView, params);
}
// create and add text view if not already there
if (_childTextView == null) {
_childTextView = new TextView(_context);
_childTextView.setTextColor(Color.BLACK);
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.addRule(CENTER_HORIZONTAL);
addView(_childTextView, params);
}
Log.e(TAG, "setting text to: " + _val);
// update value and make visible
_childTextView.setText(String.valueOf(_val));
setVisibility(VISIBLE);
Log.e(TAG, "end show()");
}
The background image displays correctly every time. The textView only displays correctly the second time show() is called and afterwards. Logging in onSizeChanged() shows that the calculated numbers are correct the first time. As expected, onSizeChanged() only gets called the first time, a bit after we return from show(). Subsequent calls to show() just set the value and visibility, and the text is displayed correctly.
My question is: is there a better way to do this? Or a better callback method to override?
Trying to set these values in show() doesn't work because the main view doesn't yet know its own size (at least the first time). I have tried putting invalidate() at the end of onSizeChanged(). I have also tried putting the call to setText() there.
I need to be able to do this based on size, because this class is reused in different contexts where the image needs to be smaller or larger.
Thank you for any insight you can give. I'd really like to keep this simple if possible.
Edit: What I am trying to do is size some text to be about 1/2 the size of the child image (which is the same as the parent size), and to have top padding set to about 1/3 of the image size. This would be easy if I just wanted it to be one size. However, I want it to be size-adjustable based on the needs of the display.
Imagine a postage stamp, where you want to place the value somewhere precisely in the image. So far so good. But what if this postage stamp needs to be displayed at different sizes on the same phone? You'd want both the placement offset (the padding) and the text size to adjust accordingly. If I hardcode this into the xml, then the text size and placement will not be adjusted when I size the layout. The text will be too big on the small version, and will be placed too far from the top of the image.
i have no idea why you override onSizeChanged(), normaly android handles all this nicely if you use it the way it is intendet.
can you pls explain what you want to achive - maybe with example picture?
however i wondered that you don't override onMesure() when you override the rest and if a delayed call to show() helps it also might be because of onMesure is called in between.
edit:
in android you should never want to know a real size of some views. nearly every device has other sizes and there is portrait/landscape mode too. if you start coding vs real sizes you can give up at the start. instead you should use something more relative like dp and sp than you should never again worry about text sizes and similar.
you may also want and you should use LayoutInflater and xml files as much as possible in your application. in a Activity you can call setContentView(). in other cases there might be methods to overload like onCreateView. and if you have nothing else you can do it like this:
LayoutInflater inflater = LayoutInflater.from(contextEgActivity);
View rootView = inflater.inflate(R.layout.highscore_daily, parentCanBeNull);
edit II:
so this is what you want - right? (on the ImageView and the TextView it would be even better to use wrap_content for height and width)
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:layout_width="200dp"
android:layout_height="200dp"
android:id="#+id/imageView"
android:layout_gravity="center"
android:background="#ff0000" />
<TextView
android:layout_width="100dp"
android:layout_height="100dp"
android:text="New Text"
android:id="#+id/textView"
android:layout_gravity="center"
android:background="#00ff00"
android:textSize="20sp" />
</FrameLayout>
if you only have ~3 different sizes i would write 3 different xml files to match what you want. otherwise i think this code will fit your needs.
//http://stackoverflow.com/questions/4605527/converting-pixels-to-dp
public static float convertDpToPixel(float dp, Context context){
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
float px = dp * (metrics.densityDpi / 160f);
return px;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);//loads the xml above
ImageView v = (ImageView) findViewById(R.id.imageView);
int dp = 200;
int px = (int) convertDpToPixel(dp, this);
v.setMaxHeight(px);//no need for it
v.setMinimumHeight(px);//should do more or less the same as next line
v.setLayoutParams(new LinearLayout.LayoutParams(px, px));//is like android:layout_width="200dp" android:layout_height="200dp"
v.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, px));//is like android:layout_width="wrap_content" android:layout_height="200dp"
//basically you can do the same with the TextView + the Text styling
TextView tv = (TextView) findViewById(R.id.textView);
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 50);
tv.setPadding(30,30,30,30);//don't forget, this is also px so you may need dp to px conversion
}
this is the normal way, nice clean and easy. if you why ever still want to react on size changes of your parent you can try this but i don't suggest it. btw changing view stuff should only be executed from ui/main thread so if the method gets called from a other thread thry a Handler like new Handler(getMainLooper)
I have a relative layout containing three textviews, each having a width of half-width of the screen. I want the user to be able to use a scroll gesture and move these textviews together, and if the textview located far left goes off-screen, it is moved to the far right next to the third textview. So I want to create a sort of a endless scroller-system.
However, using the code below results in gaps between the views when scrolling, and I think the gap widths are dependable on the scrolling speed.
Here is a link to a screenshot of the problem: http://postimg.org/image/bnl0dqsgd/
Currently I have implemented scrolling only for one direction.
XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/rel_layout"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false">
<com.app.healthview.BorderedTextView
android:id="#+id/btvYear1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:background="#color/YearColor1"
android:maxLines="1"
android:text="2012" />
<com.app.healthview.BorderedTextView
android:id="#+id/btvYear2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_alignParentTop="true"
android:layout_toRightOf="#+id/btvYear1"
android:background="#color/YearColor2"
android:maxLines="1"
android:text="2013" />
<com.app.healthview.BorderedTextView
android:id="#+id/btvYear3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/YearColor1"
android:layout_alignParentTop="true"
android:layout_toRightOf="#+id/btvYear2"
android:gravity="center"
android:maxLines="1"
android:text="2014" />
</RelativeLayout>
Then I initialize the views in a function, which is called after setting the content view:
public void InitTimeView() {
year_views = new BorderedTextView[3];
year_views[0] = (BorderedTextView) findViewById(R.id.btvYear1);
year_views[1] = (BorderedTextView) findViewById(R.id.btvYear2);
year_views[2] = (BorderedTextView) findViewById(R.id.btvYear3);
// Acquire display size
display = getWindowManager().getDefaultDisplay();
size = new Point();
display.getSize(size);
int year_width = size.x / 2;
year_views[0].setWidth(year_width);
year_views[1].setWidth(year_width);
year_views[2].setWidth(year_width);
// This is done, because when scrolling, the third view which in the beginning is off-screen, could not be seen
RelativeLayout relLayout = (RelativeLayout) findViewById(R.id.rel_layout);
relLayout.getLayoutParams().width = year_width * 4;
relLayout.invalidate();
}
Then the onScroll-method:
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
// intCurrYearMember is public, it stores the view that is next to be moved
// intRightYearMember; intCurrYearMember is located right to this view.
switch(intCurrYearMember) {
case 0:
intRightYearMember = 2;
case 1:
intRightYearMember = 0;
case 2:
intRightYearMember = 1;
}
// Move the views
for (TextView textview : year_views) {
textview.setX(textview.getX() - (distanceX / 2));
}
// Check if the view most left is now too far on left, and move it if needed to far right
if ((year_views[intCurrYearMember].getX() + year_views[intCurrYearMember].getWidth()) <= 0) {
// Is the problem here perhaps?
year_views[intCurrYearMember].setX(year_views[intRightYearMember].getRight());
intPreviousMember = intCurrYearMember;
if (intCurrYearMember < 2)
intCurrYearMember++;
else
intCurrYearMember = 0;
}
return true;
}
As it shows in the code, my idea is to build a year scroller. If someone happends to have a better, more efficient idea for how to do it, I am happy to hear your advices!
So my question is: why are there gaps between the textviews?
I would not suggest doing this at all. Utilizing Horizontal Swipes for these things is not a standard use of the platform and why waste your time developing this when there are already tested and pretty Android Components such as the Pickers that you can use easily.
A Horizontal Swiping gesture is normally saved for a Menu Drawer, View Pager, or other piece of functionality.