I have a linear layout containing an EditText and an ImageView. I've given the EditText a #null background and have given the LinearLayout a background of:
?android:attr/editTextBackground
to make it look like the whole thing is one widget. When the EditText gets focus/is selected I'd like to update the background drawable of the linear layout to show that the whole thing is selected.
My layout XML for the Linear Layout:
<LinearLayout
android:id="#+id/search_plate"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/editTextBackground">
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="#+id/edit_text"
android:layout_weight="1"
android:background="#null"
android:height="36dp"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="#+id/image_view_close"
android:src="#drawable/ic_clear"
android:focusable="true"
android:background="?android:attr/selectableItemBackground"/>
</LinearLayout>
This is the code I'm using to try and change the background state of the LinearLayout when the EditText is focused:
public class IconEditText extends LinearLayout implements View.OnFocusChangeListener {
private static final String LOG_TAG = "IconEditText";
private View mSearchPlate; // Linear Layout
private EditText mEditTextSearch;
public IconEditText(Context context) {
this(context, null);
}
public IconEditText(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.icon_edit_text, this, true);
mSearchPlate = findViewById(R.id.search_plate);
mEditTextSearch = (EditText) findViewById(R.id.editText);
mEditTextSearch.setOnFocusChangeListener(this);
}
#Override
public void onFocusChange(View view, boolean focused) {
mSearchPlate.getBackground().setState(focused ? FOCUSED_STATE_SET : EMPTY_STATE_SET);
}
}
As well as using FOCUSED_STATE_SET, I've also tried the following:
ENABLED_FOCUSED_SELECTED_STATE_SET
FOCUSED_SELECTED_STATE_SET
ENABLED_SELECTED_STATE_SET
SELECTED_STATE_SET
None of the above seemed to change the background of the LinearLayout to the blue underline. Any help would be appreciated!
Seems as though: ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET works... if anyone could explain that, that would be great!
Related
I have a Relative Layout with an EditText and an ImageView inside it.
Under certain circumstances, I would like to make the whole layout clickable and not any of its children.
I added an OnClickListener on the layout. And I tried the following with the children:
1. setEnabled(false)
2. setClickable(false)
This works for the ImageView but even after the above changes, when I click on the area near the EditText, the keyboard comes up and I can see the cursor in the edit text.
Instead of that, I am hoping that all click/touch events go to the layout.
Could some one help?
Thanks
Yon create a CustomLayout class and override the onInterceptTouchEvent method. If that method returns true, the layout's childrens will not receive the touch event. You can create a member variable and a public setter to change the returning value.
CustomLayout class
public class CustomLayout extends LinearLayout {
//If set to false, the children are clickable. If set to true, they are not.
private boolean mDisableChildrenTouchEvents;
public CustomLayout(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
mDisableChildrenTouchEvents = false;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mDisableChildrenTouchEvents;
}
public void setDisableChildrenTouchEvents(boolean flag) {
mDisableChildrenTouchEvents = flag;
}
}
MainActivity
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CustomLayout layout = findViewById(R.id.mylayout);
//Disable touch events in Children
layout.setDisableChildrenTouchEvents(true);
layout.setOnClickListener(v -> System.out.println("Layout clicked"));
}
}
XML Layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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="match_parent"
tools:context=".MainActivity">
<com.example.dglozano.myapplication.CustomLayout
android:id="#+id/mylayout"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#drawable/outline"
android:clipChildren="true"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<EditText
android:id="#+id/editText2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Enter email"
android:inputType="textEmailAddress"
android:layout_gravity="center"/>
</com.example.dglozano.myapplication.CustomLayout>
</android.support.constraint.ConstraintLayout>
I have a TextView, which I need to be centered horizontally and vertically inside a square. To do this, I have put it inside a RelativeLayout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:background="#fff"
>
<TextView
android:id="#+id/centered_text_view_text"
android:layout_centerInParent="true"
android:text="test"
android:background="#00ff00"
android:textColor="#000"
android:textSize="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</RelativeLayout>
I instantiate the RelativeLayout with a LayoutInflater and then call the layout method on the RelativeLayout to position it where I need it.
LayoutInflater inflater = Helpers.getInflater();
containerLayout = (RelativeLayout) inflater.inflate(R.layout.centered_text_view, null);
textView = (TextView) containerLayout.findViewById(R.id.centered_text_view_text);
... (later in the code)
containerLayout.layout(dayLeft, weekTop, dayRight, weekBottom);
My problem is that the TextView is not visible. I can see the white background for the RelativeLayout, but neither the text nor the background for the TextView.
I have also tried getting rid of the RelativeLayout and calling the layout method on the TextView. I haven't been able to get this to work, however, because the text is only horizontally, and not vertically, aligned after I set the gravity on the TextView.
First - Custom views should extend some sort of View.class. The class to extend should be whatever your root view is in your xml.
Second - You are keeping a reference to containerLayout (root view) which is redundant. This class is the root view.
Replace containerLayout with this or just removing entirely:
textView = (TextView) findViewById(R.id.centered_text_view_text);
Third - I can't say how you are inflating is wrong, but I have never done it like that.
Instead of calling layout on a containerLayout, try inflating with the inflate() method. (You get access to this when extending a sub class of View)
inflate(getContext(), R.layout.view_user_tag, this);
Notice how I am setting this as the root, and using the inflater from the view class.
Your class may look like something like this:
public static class TextInLinear extends RelativeLayout {
public TextView textView;
//region default constructers
public void TextInLinear(Context context){
this(context, null);
}
public void TextInLinear(Context context, AttributeSet attrs){
this(context, attrs, 0);
}
public void TextInLinear(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if(!isInEditMode()) init();
}
//endregion
private void init(){
inflate(getContext(), R.layout.view_user_tag, this);
textView = (TextView) this.findViewById(R.id.centered_text_view_text);
}
/* more methods. i.e. setText(), getText() or whatever */
}
I created a custom input method that changes layouts dynamically. I want to align the last view to the bottom of the input area. I set the layout_alignParentBottom property for the last view, but then the input area expands to fill almost the entire screen, as seen here.
Without layout_alignParentBottom set
With layout_alignParentBottom set
I do have an android:windowBackground style set, but even without it, it does the same thing. I inflate my XML layout in my custom InputMethodService class in onCreateInputView(), and I don't adjust any layout settings in the onFinishInflate() method of MyClass. Here is my code. Any ideas?
my_class_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<com.package.MyClass xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="#+id/login_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:text="#string/login" />
</com.package.MyClass>
MyClass.java
package com.package;
<imports>
public class MyClass extends RelativeLayout {
private final MyClassInputMethodService mInputMethodServiceContext;
private Button mLoginButton;
public MyClass(Context context, AttributeSet attrs) {
super(context, attrs);
mInputMethodServiceContext = (MyClassInputMethodService)context;
}
public MyClass( MyClassInputMethodService context ) {
super(context);
mInputMethodServiceContext = (MyClassInputMethodService)context;
}
#Override
protected void onFinishInflate() {
// Login button
mLoginButton = (Button)findViewById(R.id.login_button);
}
MyClassInputMethodService.java
package com.package;
<imports>
public class MyClassInputMethodService extends InputMethodService {
private static final String TAG = MyClassInputMethodService.class.getSimpleName();
private static MyClassInputMethodService mContext;
private View mInputView;
#Override
public void onCreate() {
// Set Theme
setTheme(UserManagement.getCurrentUser().getTheme().getResourceId());
super.onCreate();
mContext = this;
}
#Override
public View onCreateInputView() {
mInputView = (MyClassLogin) getLayoutInflater().inflate(R.layout.fast_fill_login, null);
return mInputView;
}
}
EDIT
I tried a smaller-sized image for the windowBackground drawable, and it did make the default input method area smaller.
<style name="Theme.Blue" >
<item name="android:windowBackground">#drawable/background_blue</item>
</style>
So that leads me to another possibility. Do I have to use the smallest possible background image size that would fit the least number of views that will be shown, or can I make the windowBackground drawable dynamic, so it will only use as much height as the input method views use?
I have a custom view that contain a framelayout. This framelayout contain two views (LinearLayout) that can be swipe. If I swipe the first one, the second one appears and vice versa. One of those views has a button but I don't know why, this button is like disable. I cannot click on it and the onClick method has no effect.
Here the structure of the layout xml inflated in the custom view :
<FrameLayout>
<LinearLayout
android:id="#+id/frontview"
/>
<LinearLayout
android:id="#+id/backview">
<Button
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="#+id/ButtonUpdate"
android:text="#string/bUpdate"
android:padding="5dp"
android:clickable="true"
style="?android:attr/borderlessButtonStyle"
/>
</LinearLayout>
</FrameLayout>
Here the code in my custom view :
public class mView extends LinearLayout {
ImageView icon;
TextView current_data;
TextView previous_data;
TextView time ;
Button bUpdate;
EditText TextUpdate;
public mView(Context context) {
super(context);
init(context);
}
public mView(Context context, AttributeSet attrs) {
super(context,attrs);
init(context);
}
public mView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// nothing
}
}
public void init(Context pContext) {
LayoutInflater inflater = (LayoutInflater) pContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View ll = inflater.inflate(R.layout.list_item_data, this, true);
/** We initialize the elements of our UI **/
/**
* First View
*/
icon= (ImageView) ll.findViewById(R.id.ic_icon);
current_data = ll.(TextView) findViewById(R.id.current_data);
previous_data = ll.(TextView) findViewById(R.id.previous_data);
time = (TextView) ll.findViewById(R.id.time);
/**
* Second View
*/
bUpdate = (Button) ll.findViewById(R.id.ButtonUpdate);
TextUpdate= (EditText) ll.findViewById(R.id.TextUpdate);
bUpdate.setOnClickListener(new bUpdateClickListener());
}
private class bUpdateClickListener implements OnClickListener {
#Override
public void onClick(View v) {
// When the button is clicked, the front view re-appears and the backview disappears
frontview
.animate()
.translationX(0);
backview
.animate()
.translationX(-backview.getMeasuredWidth());
}
}
The swipe is correctly handle with onInterceptTouchEvent(MotionEvent ev) and onTouchEvent(MotionEvent ev).
Here the main.xml used in MyActivity :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#f6f6f6">
<TextView
android:id="#+id/infoimc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="18dp"
android:layout_marginTop="8dp"
android:text="#string/app_name"
android:textColor="#000000"
android:textSize="16dp"/>
<View
android:id="#+id/divider_infoimc"
android:layout_width="match_parent"
android:layout_height="1dip"
android:layout_marginRight="18dp"
android:layout_marginLeft="18dp"
android:background="#99CC00"/>
<com.example.essai.CustomGraph
android:id="#+id/CustomGraph"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_gravity="center"
android:visibility="visible"
android:layout_marginTop="10dp"/>
</LinearLayout>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="80dp"
android:layout_gravity="left|center_vertical">
<com.example.essai.mView
android:id="#+id/CustomView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</FrameLayout>
</LinearLayout>
And MyActivity.class :
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
I don't know if the button must be handle also in the Activity ?
Thanks for your help !
The root cause
According to API:
FrameLayout is designed to block out an area on the screen to display
a single item.
If there is more items, like in your case, then "unexpected" things will happen:
Child views are drawn in a stack, with the most recently added child
on top.
This means, your frontview is on top of your backview and since the frontview doesn't have android:clickable="true" the click events (on button) are not delegated below.
Solution 1
Reorder the child-layout gravity programmatically.
You can, however, add multiple children to a FrameLayout and control
their position within the FrameLayout by assigning gravity to each
child, using the android:layout_gravity attribute.
Just switch the android:layout_gravity="top" and android:layout_gravity="bottom" whenever you are sliding them.
Solution 2
Control the visibility of the child-layouts programmatically.
When the backview should be displayed, set the visibility of the frontview to View.GONE. And set it to View.VISIBLE in the reversed case.
Solution 3
Change FrameLayout to a different layout type.
Could require more "fiddling" with layout xmls...
See what you're familliar with the most and choose the solution accordingly :)
I've implemented a custom view with hosts two subviews which are identified by an id in the xml. When using two of this custom view in the same layout I run into the problem that it is random which custom view is chosen.
How can I write a custom view with different view ids that can be multiply used in the same layout?
Here is the xml of the custom view:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<EditText
android:id="#+id/clearable_edit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="textCapWords"
android:paddingRight="35dip" />
<Button
android:id="#+id/clearable_button_clear"
android:layout_width="30dip"
android:layout_height="30dip"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="5dip"
android:background="#drawable/clear_button" />
</RelativeLayout>
The id (android:id="#+id/clearable_edit") of the EditText is the problem here.
Usage of custom view:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<com.custom.package.ClearableEditText
android:id="#+id/arr_location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
</com.custom.package.ClearableEditText>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<com.custom.package.ClearableEditText
android:id="#+id/dep_location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
</com.custom.package.ClearableEditText>
</LinearLayout>
In this example the views of type "ClearableEditText" share the same id of their EditText subview.
Here is the code for ClearableEditText:
public class ClearableEditText extends RelativeLayout {
private LayoutInflater inflater = null;
private EditText edit_text;
private Button btn_clear;
public ClearableEditText(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
initViews();
}
public ClearableEditText(Context context, AttributeSet attrs){
super(context, attrs);
initViews();
}
public ClearableEditText(Context context){
super(context);
initViews();
}
private void initViews(){
inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.clearable_edittext, this, true);
edit_text = (EditText) view.findViewById(R.id.clearable_edit);
btn_clear = (Button) findViewById(R.id.clearable_button_clear);
btn_clear.setVisibility(RelativeLayout.INVISIBLE);
}
}
First fetch parent View like this:
View v1 = findViewById(R.id.arr_location);
and then
EditText ed1 = (EditText)v1.findViewById(R.id.clearable_edit);
Similarly
View v2 = findViewById(R.id.dep_location);
EditText ed2 = (EditText)v2.findViewById(R.id.clearable_edit);
This way you can add as many ClearableEditText as you want having same id for EditText and Button. Just make sure that every ClearableEditText has different id e.g. in this case R.id.arr_location and R.id.dep_location.
I've found a solution.
I've added a method to ClearableEditText where you can set the id of the underlying EditText from outside the object and set it with a new id.
Here is a code sample:
//inside ClearableEditText
public void setEditId(int id){
edit_text.setId(id);
}
//somewhere else
departureLocation = (ClearableEditText)view.findViewById(R.id.dep_location);
departureLocation.setEditId(R.id.clearable1);
arrivalLocation = (ClearableEditText)view.findViewById(R.id.arr_location);
arrivalLocation.setEditId(R.id.clearable2);
The Ids are created with a "ids.xml" in the values folder, which causes eclipse/ADT to create a placeholder id for the entered items
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- This file is used to create unique ids for custom views, which will be used more
than once in the same layout file. Using unique ids prevents the custom view from getting
the wrong state. -->
<item name="clearable1" type="id"></item>
<item name="clearable2" type="id"></item>
</resources>