How to align the bottom of a View with the Software Keyboard? - android

I'm trying to make a LinearLayout align with the Software Keyboard when it is showing.
My LinearLayout contains an EditText to which the Software Keyboard is always being aligned.
The whole View (LinearLayout) looks like this:
When gaining focus to edit the EditText keyboard aligns itself to the EditText instead of the whole View:
I know that in the Material Design Library there is a TextInputLayout which also is a LinearLayout containing an EditText. When focusing on the EditText wrapped in the TextInputLayout the whole LinearLayout is aligned with the keyboard:
Now, I've seen people tell that I should add a windowSoftInputMode parameter in the <activity>:
android:windowSoftInputMode="adjustResize"
which does not work. Also I've seen many other answers on that issue but all of them referred to setting flags and parameters outside of the View itself but within the Activity's Window which is not what I want.
Here is the code that approximates mine - it has the dependencies that the CustomEditText has:
public class TestEditText extends LinearLayoutCompat {
public TestEditText(#NonNull Context context) {
super(context);
initTestEditText();
}
public TestEditText(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
initTestEditText();
}
public TestEditText(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initTestEditText();
}
// Layout in which there is an EditText - additional views, like buttons can be here
private LinearLayoutCompat textInputLayout;
private AppCompatEditText editText;
private void initTestEditText() {
setOrientation(VERTICAL);
setGravity(Gravity.CENTER_VERTICAL);
setBackgroundColor(Color.BLUE);
// Getting states from children - in this case from another LinearLayout
setAddStatesFromChildren(true);
prepareTextInputLayout();
prepareEditText();
}
private void prepareTextInputLayout() {
textInputLayout = new LinearLayoutCompat(getContext());
textInputLayout.setLayoutParams(
new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dpToPx(128))
);
textInputLayout.setGravity(Gravity.CENTER_VERTICAL);
// Getting the states from children - in this case from an editText
textInputLayout.setAddStatesFromChildren(true);
addView(textInputLayout);
}
private void prepareEditText() {
editText = new AppCompatEditText(getContext());
LayoutParams params = new LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.MATCH_PARENT,
1
);
editText.setLayoutParams(params);
editText.setGravity(Gravity.TOP);
editText.setBackgroundDrawable(null);
editText.setMaxLines(Integer.MAX_VALUE);
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
editText.setTextSize(14);
textInputLayout.addView(editText);
}
private int dpToPx(int dp) {
return (int) (dp * getResources().getDisplayMetrics().density);
}
}
There has to be some way to make the bottom of a View align with the Software Keyboard, because TextInputLayout does exactly that without setting any flags in the <activity>...
If You have any other idea, please help.

Related

Custom Relative layout not rendering any children

Intro:
I am attempting to add various Views to my custom RelativeLayout, i.e. Buttons, ImageViews, etc however none of them render/show.
Documentation:
As shown on numerous SO questions: here, here, here, here and many more,
I have the standard requirements for extending a layout, i.e. the 3 constructors, that being:
public RelativeLayout(Context context) {}
public RelativeLayout(Context context, AttributeSet attrs) {}
public RelativeLayout(Context context, AttributeSet attrs, int defStyleAttr){}
referred to here on Android Developer site.
Implementation:
My RelativeLayout named DiceRoller has the following implementation:
public class DiceRoller extends RelativeLayout {
private DieContainer dieContainer;
private Context mContext;
private int speed;
private Runnable moveDies;
private Handler handler;
private Timer timer;
public DiceRoller(Context context) {
super(context);
mContext = context;
init();
}
public DiceRoller(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
init();
}
public DiceRoller(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
init();
}
private void init() {
//source : https://stackoverflow.com/questions/28265286/custom-relative-layout-not-showing-child-views
setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
setGravity(Gravity.CENTER);
ImageView mainImage = new ImageView(mContext);
mainImage.setId(1994);
LayoutParams params = new LayoutParams(100, 100);
mainImage.setImageResource(R.drawable.die1);
mainImage.setLayoutParams(params);
addView(mainImage);
RelativeLayout.LayoutParams crossParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
crossParams.addRule(RelativeLayout.ALIGN_TOP | RelativeLayout.ALIGN_LEFT, mainImage.getId());
ImageView crossImage = new ImageView(mContext);
crossImage.setImageResource(R.drawable.die6);
crossImage.setLayoutParams(crossParams);
addView(crossImage);
TextView tv = new TextView(mContext);
tv.setText("hello world");
addView(tv);
}
}
Please Note: the contents of the init() method was purely to test if views were infact rendered. This was my last attempt at debugging the issue, previously I added views from my MainActivity aswell, obviously without success
With an associated layout file
<?xml version="1.0" encoding="utf-8"?>
<com.myapp.DiceRoller
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="1000px"
android:layout_height="1000px"
android:background="#color/colorAccent"
android:id="#+id/rollerBack">
</com.myapp.DiceRoller>
What is the problem:
The problem is simple. No child of the layout is rendered/shown/visible.
I attempted adding a child in my MainActivity, programmatically. It did not render. I attempted adding a child within this RelativeLayout class, it did not render.
Additional Info:
note: When adding views, I always added text or some image, I also set the X, Y values, also included RelativeLayout.LayoutParams() with the wrap option set.
When debugging this issue, if I added a view (ImageView, Button, etc), the layout has each child stored, and each child's parent is this RelativeLayout. Each child has a width, height, X, Y value and some content (either an image or text), thus the problem does not lie with the children.
I am at a loss, I have no idea why it doesn't render, any help would be greatly appreciated!

View ignores alpha value after View.VISIBLE

I have a button ,with alpha set to 0.5, and its visibility is gone in the layout.
<Button
android:id="#+id/Button"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#color/black_color"
android:alpha="0.5"
android:visibility="gone"/>
At some point, I wish to make it visible ( Button.setVisibility(View.VISIBLE); ), but when I do - it's not half-transparent (0.5). It appears as if alpha is set to 1.
This problem is usually the result of having android:animateLayoutChanges="true" in the parent view. The reason is that the layout animation of setting visibility also changes the alpha of the view and overrides the change made by setAlpha.
To resolve this you can either remove android:animateLayoutChanges="true" from the parent or create a custom view to set the visibility in onVisibilityChanged like this:
public class AlphaView extends View {
private static final String TAG = AlphaView.class.getSimpleName();
public AlphaView(Context context) {
super(context);
}
public AlphaView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public AlphaView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public AlphaView(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onVisibilityChanged(#NonNull View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
if (visibility == VISIBLE) {
setAlpha(0.5f);
}
}
}
I ran into this problem as well. It seems to be a bug in Android itself. My solution was to avoid setting visibility, and to adjust alpha only. My view has a visibility of 'visible' in the XML, and starts off with the XML alpha tag value set to 0.0. Then, when I want it to be visible, I adjust the alpha programmatically:
dimmerView.setAlpha(.15f);
I disappear it by setting the alpha again to zero. Theoretically, you might need to adjust various views position on the z-axis with bringToFront (and in the case of a button you might want to remove its listener when alpha is set to zero), but in my implementation it did not seem to be necessary.
After setting Button Gone to Visible, add Alpha of the Button
like :
buttonObject.setAlpha(.5f);

Automatically disable HorizontalScrollView when content small enough

I use a HorizontalScrollView to contain a bunch of dynamic TextView elements. They are dropped into a LinearLayout container that is the only child of the scroll view:
<HorizontalScrollView android:id="#+id/outline_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none"
android:requiresFadingEdge="horizontal"
android:fadingEdgeLength="16dp">
<LinearLayout android:id="#+id/outline"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</HorizontalScrollView>
This is to ensure that if (and only if) there's more text than the available width can show, the user can scroll horizontally through the texts.
BUT: in many, many cases, the texts are short enough to be shown on screen. The LinearLayout container with id outline thus fits completely within the HorizontalScrollView.
Problem is: horizontal swipe gestures are still caught but should not be, because the whole thing is within a ViewPager which itself would like to handle the horizontal swipes!
I am looking for a solution that enables this HorizontalScrollView's scrolling only if the room for the contents is too limited.
In order to prevent the HorizontalScrollView from scroll, you have to override the onTouchEvent method to return false. That led me to create my own HSV like so:
public class MyHorizontalScrollView extends HorizontalScrollView{
boolean tooSmall = true;
public MyHorizontalScrollView(Context context) {
super(context);
}
public MyHorizontalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyHorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setTooSmall(boolean tooSmall){
this.tooSmall = tooSmall;
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
if(tooSmall)
return false;
else
return super.onTouchEvent(ev);
}
}
Then, after you replace your HSV with this custom view, you need monitor the size of your LinearLayout(R.id.outline) to see if it is smaller or larger than your HSV. Adding this snippet helped me achieve that goal.
ll = (LinearLayout) view.findViewById(R.id.outline);
hsv = (MyHorizontalScrollView) view.findViewById(R.id.outline_container);
ll.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Log.d("widths", ll.getWidth() + " : " + hsv.getWidth());
hsv.setTooSmall(ll.getWidth() < hsv.getWidth());
}
});

Android TextView, autoLink="all" showing all numbers as clickable

I have a Scrollview and I have the attribute android:clickable="true" and android:autoLink="all".
I have a string for the ScrollView, and the emails, tel numbers etc, appear and are correctly clickable.
However, The string contains other numbers, such as Years, which also appear clickable and I don't want this; how can I stop this from happening?
Don't use autoLink="all", use the ones you need.
android:autoLink="web|email|phone" will probably cover your use cases.
The clickable="true" on the ScrollView isn't needed for this; rather you should set the autoLink attribute on the TextViews themselves; perhaps extracting a style if you have other common properties.
Add the new Linkify class to your project. From a place that you have access to the TextView (e.g. the Activity):
TextView myTextView = // get a reference to your textview
int mask = Linkify.ALL;
Linkify.addLinks(myTextView, mask);
The addLinks(TextView, int) method is static, so you can use it without creating an instance of Linkify. The return value (boolean) indicates whether something was linkified, but you probably don't need this information, so we don't bother with it.
You'll need to ensure that you don't put the autoLink attribute on the TextViews, otherwise the setText(...) implementations will still linkify years (unless you completely override the setText(...) implementations without calling super.setText(...))
For extra brownie points, you can create a subclass of TextView which will do the linkify for you when you set text on it:
public class AutoLinkifyTextView extends TextView {
public AutoLinkifyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AutoLinkifyTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void setText(String text) {
super.setText(text);
parseLinks();
}
#Override
public void setText(int stringRes) {
super.setText(stringRes);
parseLinks();
}
private void parseLinks() {
Linkify.addLinks(this, Linkify.ALL);
}
}
For top marks of course, you'd read the attributes from the attrs and use the correct mask from the XML attributes, but I'd prefer to get rid of that option and do it here.

Android: Using FEATURE_NO_TITLE with custom ViewGroup leaves space on top of the window

I am trying to create a custom ViewGroup, and I want to use it with a full screen application. I am using the "requestWindowFeature(Window.FEATURE_NO_TITLE)" to hide the title bar. The title bar is not showing, but it still consuming space on top of the window.
The image above was generated with the following code:
public class CustomLayoutTestActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
Button b = new Button(this);
b.setText("Hello");
CustomLayout layout = new CustomLayout(this);
layout.addView(b);
setContentView(layout);
}
}
public class CustomLayout extends ViewGroup {
public CustomLayout(Context context) {
super(context);
}
public CustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.i("CustomLayout", "changed="+changed+" l="+l+" t="+t+" r="+r+" b="+b);
final int childCount = getChildCount();
for (int i = 0; i < childCount; ++i) {
final View v = getChildAt(i);
v.layout(l, t, r, b);
}
}
}
(The full Eclipse project is here)
It is interesting to see that it is the Android that is given this space for my custom layout. I am setting the CustomLayout as the root layout of my Activity. In the Log in the "onLayout" is receiving "t=25", and that is what is pushing my layout down. What I don't know is what I am doing wrong that makes Android the "t=25" (which is exactly the height of the title bar).
I am running this code in the Android SDK 2.1, but I also happens in Android 2.2.
EDIT: If I change the CustomLayout class for some default layout (such as LinearLayout), the space disappears. Of course, the default layouts of Android SDK don't create the layout I am trying to create, so that is why I am creating one.
Although the layout I am creating is somewhat complex, this is the smallest code I could create reproducing the problem I have with my layout.
It's not a full answer, but in the meantime you can work around the problem by wrapping your custom layout in a <FrameLayout />
Also, it's worth noting that your layout extends beyond the bottom of the screen. It's shifted down by the title bar height (38 pixels in my emulator)
Edit: Got it. onLayout() (and the corresponding layout() method) specify that the coordinate are not relative to the screen origin, they're relative to the parent ( http://developer.android.com/reference/android/view/View.html#layout%28int,%20int,%20int,%20int%29 ). So the system is telling you that you're at relative coordinates (0, 38), and you're adding it when passing that down to your child, which means that you're saying that your child is at screen coordinates (0, 76), causing the gap.
What you actually want to do is:
v.layout(0, 0, r - l, b - t);
That will put your child Views aligned with the top left corner of your View, with the same width and height as your view.
I had the same issue with a FrameLayout in 2.2
I fixed it by adding android:layout_gravity="top" to the FrameLayout

Categories

Resources