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 */
}
Related
I've got a custom view for my app named AvatarView:
<?xml version="1.0" encoding="utf-8"?>
<com.ulouder.views.AdvancedRelativeLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_gravity="center_horizontal"
android:layout_margin="0dp"
android:padding="0dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CP"
android:id="#+id/initialsView"
android:layout_alignTop="#+id/avatarView"
android:layout_alignLeft="#+id/avatarView"
android:layout_alignBottom="#+id/avatarView"
android:layout_alignRight="#+id/avatarView"
android:background="#drawable/avatar_background"
android:textColor="#color/white"
android:gravity="center"
android:textStyle="bold"
android:textSize="8sp" />
<com.makeramen.roundedimageview.RoundedImageView
app:riv_corner_radius="20dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/avatarView"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginTop="4dp"
android:layout_marginLeft="4dp"
android:layout_marginBottom="4dp"
app:riv_border_color="#color/lightGray"
app:riv_border_width="0.2dp" />
</com.uLouder.views.AdvancedRelativeLayout>
AdvancedRelativeLayout is just a superclass of RelativeLayout with a small fix, nothing special there. Then, I've created a view that uses my custom view:
<?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="match_parent">
<com.ulouder.views.AvatarView
android:layout_width="50dp"
android:layout_height="50dp"/>
</LinearLayout>
Nothing fancy either. But in the designer view of the second layout XML, I'm getting this:
The editor displays my view hierarchy like it has a nested instance of itself, while clearly there isn't. If I delete either one, they both get deleted. If I declare attributes on one of them, other also gets it. They are clearly the same instance. The only exception is setting an ID. Then the problem disappears, and only single instance is displayed as expected.
I've rebuilt the project, restarted Android Studio, but it's still the same. What am I doing wrong?
UPDATE: Nope, now, after editing id, the problem still continues again.
UPDATE 2: It's not just a layout so I can't use <include> tag. It's a custom view which has custom logic inside.
UPDATE 3: Here is my custom view's (relevant) code:
public class AvatarView extends FrameLayout {
public AvatarView(Context context) {
super(context);
init();
}
TextView initialsView;
RoundedImageView imageView;
public AvatarView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
void init(){
inflate(getContext(), R.layout.view_avatar, this);
initialsView = (TextView) findViewById(R.id.innerInitialsView);
imageView = (RoundedImageView) findViewById(R.id.innerImageView);
}
#SuppressWarnings("SuspiciousNameCombination")
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec); //always square
imageView.setCornerRadius(widthMeasureSpec / 2f);
initialsView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, widthMeasureSpec * 30f);
}
}
UPDATE 4: It appears that this happens wherever I put my custom AvatarView class, not just at one place.
I did not find any reason to inflate the same view inside your class constructor method after checking the custom views documentation. Try to remove the inflate inside your init method.
...
public AvatarView(Context context) {
super(context);
init();
}
...
public AvatarView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
void init(){
// inflate(getContext(), R.layout.view_avatar, this);
initialsView = (TextView) findViewById(R.id.innerInitialsView);
imageView = (RoundedImageView) findViewById(R.id.innerImageView);
}
...
As the title, i want to know if there is a the best way to control a view that added dynamically. (we have to keep reference to the view that was added)
Some time, for a complex request we have to add view in runtime. The is some ways to do that. In my case:
Some time i use a listview/recyclerview and control view via the list/recycleview adapter.
Other way is use a hashmap.
Do you have any other ideas? and how it work?
I prefer way, when I define View both by Java and XML file. View created like this, gives you ability to call your own Java methods, but you don`t need to create whole layout dynamically in Java. Little example:
MyView.java:
public class MyView extends LinearLayout {
TextView textView;
public MyView(Context context) {
super(context);
init();
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public void init() {
inflate(getContext(), R.layout.my_view, this);
setOrientation(VERTICAL);
textView = (TextView) findViewById(R.id.text_view);
}
public MyView setContent(String value) {
textView.setText(value);
return this;
}
}
my_view.xml:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="#+id/text_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</merge>
After that, you can simply add it in you layout through Java:
cont.addView(new MyView(this).setContent("Value"));
or xml:
<com.path.to.your.view.MyView
android:id="#+id/my_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
How do I add a hint on the top of a listView like "Pull down to refresh" which is contained in a swipeRefreshLayout from android.support.v4.
The pull down to refresh works but I want to add a text whenever the user pulls the listview slightly down.
EDIT 10/21/2014
If you update the support-v4 to the latest version (at least 21.0.0) you can use the built-in loading indicator!
I just came up with a simple, yet effective, solution.
The idea is to add a custom ViewGroup that grows its height when the SwipeRefreshLayout child gets pulled down. In this ViewGroup you will put everything you need for your hint (TextViews, ImageViews, ...).
I chose to extend a RelativeLayout because it makes easier to position your "hint" elements.
1) Create a custom widget as follows:
public class SwipeRefreshHintLayout extends RelativeLayout {
public SwipeRefreshHintLayout(Context context) {
super(context);
}
public SwipeRefreshHintLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SwipeRefreshHintLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setSwipeLayoutTarget(final SwipeRefreshLayout swipeRefreshLayout) {
final View swipeTarget = swipeRefreshLayout.getChildAt(0);
if (swipeTarget == null) {
return;
}
swipeTarget.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
private Rect oldBounds = new Rect(), newBounds = new Rect();
#Override
public boolean onPreDraw() {
newBounds.set(swipeTarget.getLeft(), swipeRefreshLayout.getTop(), swipeTarget.getRight(), swipeTarget.getTop());
if (!oldBounds.equals(newBounds)){
getLayoutParams().height = newBounds.height();
requestLayout();
oldBounds.set(newBounds);
}
return true;
}
});
}
}
2) In your Fragment or Activity layout use this custom widget.
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.widget.SwipeRefreshHintLayout
android:id="#+id/swipe_hint"
android:layout_width="match_parent"
android:layout_height="0dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="#string/label_swipe_to_refresh"/>
<!-- Any other view positioned using RelativeLayout rules -->
</com.example.widget.SwipeRefreshHintLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipe_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>
3) Then, in your Activity onCreate() or in your Fragment onCreateView(), put those lines:
mSwipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipe_container);
mSwipeRefreshHintLayout = (SwipeRefreshHintLayout) rootView.findViewById(R.id.swipe_hint);
mSwipeRefreshHintLayout.setSwipeLayoutTarget(mSwipeRefreshLayout);
Done!
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!
I have created a custom textview class and i am trying to inflate it in my main xml. Here is my code :-
public class CustomTextView extends TextView{
public CustomTextView(Context context) {
super(context);
LayoutInflater li = LayoutInflater.from(context);
TextView view = (TextView) li.inflate(R.layout.customtextview,null);
view.setBackgroundColor(Color.RED);
}
}
customtextview.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:text="sgfiughbjkh"
android:id="#+id/customtext"
android:layout_height="match_parent" >
</TextView>
and in my main activity xml, i have only one linear layout :-
LinearLayout main = (LinearLayout)findViewById(R.id.mainView);
CustomTextView cus = new CustomTextView(this);
main.addView(cus);
I know if i extend it to linear layout instead of textview and add a lienarlayout as parent and in it that textview, it works.
But the problem is that i want to inflate an xml with only a textview and inflate it and the above code is not working.
Please suggest
How do inflate a xml containing only one textview using layout inflator ?
// inflate text view
TextView textView = (TextView) getLayoutInflater().inflate(R.layout.customtextview, null);
// add it to your view
// in your condition, we will add it to Linear Layout
LinearLayout main = (LinearLayout)findViewById(R.id.mainView);
main.addView(textView);
It's old and 2 years later, I ran into the same issue.
You cannot extend from a View and inflate from a layout. The view needs to be wrapped by a ViewGroup . Read Understanding Android's LayoutInflater.inflate()
I came up with 2 solutions , either :
1. in your class CustomTextView , you set programmatically all the properties . Don't inflate anything. Good luck with conversion from resources dimen.xml
2. In your Activity , inflate the Textview
LinearLayout main = (LinearLayout)findViewById(R.id.mainView);
TextView textView = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.customtextview, null);
// you might need as well to set the layout params again
// LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
// textView.setLayoutParams(params);
main.addView(textView);
A bit late but maybe it will help people.
first and like Pratik goyal said you need to declare your CustomTextView and not a TextView
<com.example.CustomTextView
android:id="#+id/customTextView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Then in your CustomTextView class you need to had those lines (Constructor):
public FontFitTextView(final Context context) {
super(context);
}
public FontFitTextView(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
And last (correct me if i'm wrong people), but you can't inflate a customView into a TextView.
TextView textView = (TextView) getLayoutInflater().inflate(R.layout.customtextview, null);
rather you need to put your R.layout.customTextView into a customTextView
CustomTextView customTxtView = (CustomTextView) getLayoutInflater().inflate(R.layout.customlayout, null);
And now that i'm writing this down i don't think it's possible to inflate only a TextView. If you want to inflate R.layout.customlayout you need to have at least a layout (Linear,Relative etc...) that wrap your customTextView
Something like this
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.yourpakagename.CustomTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/textView"
android:layout_centerInParent="true"
android:includeFontPadding="false"
android:text="13" />
</RelativeLayout>
You can find all of your custom created views under heading "Custom & Library Views" in the Graphical interface of Eclipse.
If your CustomTextView is in the package "com.example" than you can define your custom component as :
<com.example.CustomTextView
android:id="#+id/customTextView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>