How do you create an XML-friendly control based on a layout? - android

I've created a custom control which is a subclass of LinearLayout. I have also created a layout file on which this control is based. Finally I have defined attributes which I parse in the constructor to set my custom properties on. As an example, one of those properties is called 'text'.
Here's a simplified version of my code (I've stripped out a lot of the other properties and such so we can just focus on the one property 'text'):
First, the class (our custom version of a RadioButton)...
public class RadioButton extends LinearLayout
{
private TextView textView;
public RadioButton(Context context, AttributeSet attrs)
{
super(context, attrs);
initAttributes(attrs, 0);
}
private void initAttributes(AttributeSet attrs, int defStyle)
{
final TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CheckBoxView, defStyle, 0);
text = a.getString(R.styleable.RadioButton_text);
if(text == null)
text = "Not set";
a.recycle();
}
#Override
protected void onFinishInflate()
{
super.onFinishInflate();
textView = (TextView)findViewById(R.id.textView);
textView.setText(text);
}
private String text;
public String getText() { return text; }
public void setText(String newValue)
{
text = newValue;
if(textView != null)
textView.setText(text);
}
}
Here's the attrs.xml file...
<resources>
<attr name="text" format="string" />
<declare-styleable name="RadioButton">
<attr name="text" />
</declare-styleable>
</resources>
And here's the 'reusable_radiobutton.xml' layout file (Note: the internal RadioButtonView is a custom-rendered View and works fine):
<?xml version="1.0" encoding="utf-8"?>
<com.somedomain.reusable.ui.RadioButton
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<com.somedomain.reusable.ui.RadioButtonView
android:id="#+id/radioButtonView"
style="#style/DefaultRadioButtonView" />
<TextView
android:id="#+id/textView"
style="#style/DefaultRadioButtonText" />
</com.somedomain.reusable.ui.RadioButton>
With the above, users of my control can simply include it in their own layout files, like so...
<include android:id="#+id/someRadioButton"
layout="#layout/reusable_radiobutton" />
The in their code, using the following, they can get that instance and do with it what they wish, like so...
RadioButton someRadioButton = (RadioButton)findViewById(R.id.someRadioButton);
someRadioButton.text = "Woot!";
This works as expected.
However, this doesn't...
<include android:id="#+id/someRadioButton"
layout="#layout/reusable_radiobutton"
app:text="Hello World!" />
It gives me a warning, but otherwise ignores it.
I then tried this...
<com.somedomain.reusable.ui.RadioButton
app:text="Hello World!" />
While this does instantiate my control and does pass 'Hello World!' to my property via the attributes, nothing actually loads or even associates the layout file to my class so nothing appears on the screen!
So how can I create a custom view, based on a layout, which other developers can simply reference in their own layout files while also allowing them to set custom attributes?
Hope that all made sense! :)
Note: The Android documentation talks about exactly what I'm after, 'Compound Controls' as referenced here, but they don't give an example of using a layout to define the compounded control. However, I feel that was pretty close.

Found it here on SO. I didn't realize you could inflate something into yourself. That and using the Merge tag took care of it for me.

Related

I need help in the custom Font and TextView

I'm new in android working on an app that gives the user info about all font's of Google.
For that, I need to make an app with a TextView Something like this
On click of the TextView, the font will change With text.
I'm thinking about using onclicklistener
you can put "your_font.ttf" file in asset folder then import it with
Typeface custom_font_1 = Typeface.createFromAsset(getAssets(), "your_font.ttf");
then assign it to your showCaseTextView with this
showCaseTextView.setTypeFace(custom_font_1);
then in your onClickListener of showCaseTextView to change your specifiedTextView font do like this
specifiedTextView.setTypeFace(custom_font_1);
and repeat it for other fonts.
You can implement your own custom font with TextView, EditText, Button etc.. by using android attributes.
How to
-Here are some steps to use:
1.Create attribute file (res->values->attrs.xml)
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<declare-styleable name="TextElement">
<attr name="font" format="string"/>
<attr name="underline" format="boolean"/>
</declare-styleable>
</resources>
2.Create Custom TextView class (anywhere in java folder)
3. Use attributes inside your layout file
4. and just run your code.
Here is the full example of your question, you can go through this exmaple:
Full Demonstration
Have 2 way to archive this
1st way
public class FontCache {
private static HashMap<String, Typeface> fontCache = new HashMap<>();
public static Typeface getTypeface(String fontname, Context context) {
Typeface typeface = fontCache.get(fontname);
if (typeface == null) {
try {
typeface = Typeface.createFromAsset(context.getAssets(), fontname);
} catch (Exception e) {
return null;
}
fontCache.put(fontname, typeface);
}
return typeface;
}
}
This caches the fonts while minimizing the number of accesses to the assets. Now, since we've a method to access our custom font, let's implement a class, which extends TextView.
Extending TextView
Next, we'll create a new Java class, which extends TextView. This allows us to use that class in all XML views. It inherits all functionality and properties of a regular TextView; but adds our custom font.
Once again, we're taking a peek at the source code of our eat foody project. The code might look complex for a second, but is straight-forward:
public class EatFoodyTextView extends TextView {
public EatFoodyTextView(Context context) {
super(context);
applyCustomFont(context);
}
public EatFoodyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
applyCustomFont(context);
}
public EatFoodyTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
applyCustomFont(context);
}
private void applyCustomFont(Context context) {
Typeface customFont = FontCache.getTypeface("SourceSansPro-Regular.ttf", context);
setTypeface(customFont);
}
}
The first three methods are just constructors, which we override to call a single method applyCustomFont(). That method is the important piece of the puzzle. It simply gets the (hopefully cached) font from our FontCache class. Lastly, we've to call setTypeface() with the font and we're almost done. In case you're wondering, we can call the setTypeface() directly (and not on a TextView object), since we're extending the TextView class.
Using the Class
You might wonder, if so much preparation is worth the effort. In this section you'll see that it is indeed. Because all you've left to do is use the class in an XML view and it automatically has your custom font. There is no Java code necessary!
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.futurestudio.foody.views.EatFoodyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/eat_foody_green_dark"
android:textSize="20sp"
android:text="Future Studio Blog"
android:layout_marginBottom="24dp"/>
</RelativeLayout>
As you can see, you can continue to use all niceties (e.g. textSize, textColor) of TextView. Now, just replace all elements with the class we just created, for example and you applied your custom font everywhere!
(Ref: https://futurestud.io/tutorials/custom-fonts-on-android-extending-textview)
2nd way
Follow Google guide support from API 26 (Android 8) https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml
Make change between textview to change font
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textview_normal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/eat_foody_green_dark"
android:textSize="20sp"
android:text="Future Studio Blog"
android:layout_marginBottom="24dp"/>
<com.futurestudio.foody.views.EatFoodyTextView
android:id="#+id/textview_custom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/eat_foody_green_dark"
android:textSize="20sp"
android:text="Future Studio Blog"
android:visibility="gone"
android:layout_marginBottom="24dp"/>
</RelativeLayout>
attention at android:visibility="gone"
in Activity you use this code to toggle between 2 TextViews
final TextView normalTextView = findViewById(R.id.textview_normal);
final TextView customTextView = findViewById(R.id.textview_custom);
normalTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
normalTextView.setVisibility(View.GONE);
customTextView.setVisibility(View.VISIBLE);
}
});
customTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
normalTextView.setVisibility(View.VISIBLE);
customTextView.setVisibility(View.GONE);
}
});

Add static text before or after a TextView

I'm new to Android Dev and I try to figure a gently way to add static text before and after a dynamic part in a TextView, without having to add 2 other TextView or add these content each time I update the dynamic part of my TextView.
Something just like ::after or ::before with content propertie in CSS.
Something which could look like this :
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="10"
android:textAfter=" km/h" />
I digged Google up and down (went to 3rd page sometimes) but I couldn't find any way to do this trick which appear pretty basic to me.
In my opinion, you just need a custom TextView. Then you can make one.
First, declare custom attributes for your CustomTextView. Create a new file named attrs.xml in /res/values. fill it with code below.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CustomTextView">
<attr name="textAfter" format="string"/>
</declare-styleable>
</resources>
Second, Create Your CustomTextView as a java file. That's simple. You just need get the value of attribute of textAfter and set it to the tail of the text when setText is invoked.
public class CustomTextView extends TextView {
private String mTextAfter;
public CustomTextView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CustomTextView);
mTextAfter = array.getString(R.styleable.CustomTextView_textAfter);
array.recycle();
}
#SuppressLint("SetTextI18n")
#Override
public void setText(CharSequence text, BufferType type) {
super.setText(text + (TextUtils.isEmpty(mTextAfter) ? "" : mTextAfter), type);
}
}
Thrid, use the CustomTextView in layout.(Do NOT forget to add xmlns:app="http://schemas.android.com/apk/res-auto" in the root view of your layout)
<!-- your package name -->
<com.xxx.xxx.CustomTextView
android:id="#+id/mtv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:textAfter=" km/h" />
Fourth, use it in XXXActivity.java.
//TextView mtv = (TextView) findViewById(R.id.mtv));// this is worked too
CustomTextView mtv = ((CustomTextView) findViewById(R.id.mtv));
mtv.setText("18");//do NOT put int value of 18 in it, or it will be treated as a resource id
Then you will see, the km/h is added after 18.
You can use string resource for dynamic text with a predefined format using %d or %1$d.
For example, if you want to show something like 10 km/h, you can use string resource like this:
<string name="range_in_kilometer_per_hour">%d km/h</string>
Then you can set it via code with:
TextView tvKm;
...
int currentKm = 10;
String kmValue = getString(R.string.range_in_kilometer_per_hour, currentKm);
tvKm.setText(kmValue);
Or if you want to show something like 10 km each 1 hour, you can use something like:
<string name="range_in_kilometer_per_hour">%1$d km each %2$d hour</string>
Then set it to your TextView with:
int currentKm = 10;
int hour = 1;
String kmValue = getString(R.string.range_in_kilometer_per_hour, currentKm, hour);
tvKm.setText(kmValue);
If you want to use string instead of an integer, you can use %s for single text replacement, or %1$s for multiple text replacement.
You need to remember that the means of 1 in %1$s is the position of the text replacement.
Read more at:
String Resources
getString
I think you should do something like this
remove text in xml i.e. km/hr
go to your activity java file let it be activity_main.java
Dynamically get the value of speed
say for
int speed;
then in textview(xml)
assign any id android:id="#+id/speedTextView"
in java
assign speed value
TextView speedWithUnit = (TextView) findViewById(R.id.speedTextView);
and
speedWithUnit.setText(speed+"km/hr");
Hope this help

Android xml global custom tag without subclassing

I know how to create a custom style attribute definition and get the value in a custom view (from this link):
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView, defStyle, 0);
String str = a.getString(R.styleable.MyCustomView_my_custom_attribute);
But I´m wondering if it´s possible to define a "global" tag that I can apply to any view, either custom or from Android SDK.
Let´s say I want to execute a static method to the views containing that attribute, but avoiding any reference to the view (like FindViewById):
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
whatever:custom_global_tag="key" />
MyStaticClass.Process(View view, String key) {}
Is this even possible?
Edit
Just to clarify, what I really need is a string. The goal is transforming an string into another and assign the new value to the text property.
My env is Xamarin but any native approach could be translated to Xamarin so all suggestions are welcome
Try Theme?
A theme is a style applied to an entire Activity or application, rather than an individual View (as in the example above). When a style is applied as a theme, every View in the Activity or application will apply each style property that it supports.
Note: just work for property the view supports.
Edit #1
if you want to pass a different string on each view, just use android:tag
something like:
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="key" />
and get the string
String key = textView.getTag();
Edit #2
set a tag to identify which view you want.
in the activity, iterate all the views to find the identified view like
final ViewGroup viewGroup = (ViewGroup)((ViewGroup)findViewById(android.R.id.content)).getChildAt(0);
for(int i = 0; i < viewGroup.getChildCount(); i++) {
View childView = viewGroup.getChildAt(i);
if ("identificaton".equals(childView.getTag())) {
// do some work to child view.
}
}
the viewGroup part can check the post
I haven't tried it myself but theoretically, you can do it. See the Calligraphy library for an example.
You can declare global attributes (like they do in the Framework) directly in a values resource file:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="myCustomAttr" format="string"/>
</resources>
Then you can add it to any view without any prefix (you will have a Lint error though):
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
myCustomAttr="key"
tools:ignore="MissingPrefix" />
And finally, you should be able to retrieve it from your view AttributeSet (given in the view constructor).
String myCustomValue = attrs.getAttributeValue(R.attr.myCustomAttr);
There is a problem if you don't create a Custom View though: to my knowledge, you don't have access to the AttributeSet after a View is created. You will have to hook yourself somewhere during the layout inflation...
You can do this with a #BindingAdapter method, using the Data Binding Library.
Based on your help (#assistne) I ended up with a recursive method that will scan the whole view tree looking for views with a tag starting with "translate:". So far it works like a charm:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tag="translate:TextWithLineBreakCharacters" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:tag="translate:Animals"/>
Helper class (Xamarin/C#):
public static class I18NAndroidHelper
{
public static void Translate(object view)
{
if(view is TextView)
{
var textView = (TextView)view;
var tag = (string)textView.Tag;
if(tag != null && tag.StartsWith("translate:", StringComparison.Ordinal))
{
var key = tag.Split(new [] {":"}, StringSplitOptions.None)[1];
textView.Text = key.Translate(); // Translate() is an string extension method
}
}
else if(view is ViewGroup)
{
var viewGroup = (ViewGroup)view;
var childCount = viewGroup.ChildCount;
for(var i = 0; i < childCount; i++)
Translate(viewGroup.GetChildAt(i));
}
else if(view is Activity)
{
var activity = (Activity)view;
var content = activity.FindViewById<ViewGroup>(Android.Resource.Id.Content);
Translate(content);
}
}
}
On Activity OnCreate:
I18NAndroidHelper.Translate(this);

EditView must contain data to further process

I have a EdidText and a Button in a Fragment. When I click the Button i want to checks whether the EditText is empty or not. If is empty I want to return an error message, If notEmpty means move to other fragments or something further process.
I previously search the solution for my problem, but mostly I found the solution like ,
if(textView.getText().toString().trim().equals("")) {
textView.setError("something");
} else {
//do something;
}
The above code worked correctly, but what I ask, how to do this in a layout not programmatically.
I want to know is this is achieved through the layout xml file or not. If possible means please give me an example.
Thank you in advance.
You can't set an error in with XML.
you need to set it in the onCreate of your fragment. This way you won't see the difference if it is set in xml or in code.
You could write your own EditText and add an extra style attribute "error". After that you need to use your own EditText and again set the error in the onCreate.
Create a new class and extend it from EditText like this:
public class MyEditText extends EditText {
public MyEditText(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyEditText);
String error = a.getString(R.styleable.MyEditText_error);
a.recycle();
setError(error);
}
}
In Attr add this:
<declare-styleable name="MyEditText">
<attr name="error" format="reference|string" />
</declare-styleable>
After that you will need to always use MyEditText
Check it with this code
editText.getText().toString().isEmpty()

Is there a way to style a TextView to uppercase all of its letters?

I would like to be able to assign a xml attribute or style to a TextView that will make whatever text it has in ALL CAPITAL LETTERS.
The attributes android:inputType="textCapCharacters" and android:capitalize="characters" do nothing and look like they are for user inputed text, not a TextView.
I would like to do this so I can separate the style from the content. I know I could do this programmically but again I want keep style out of the content and the code.
I though that was a pretty reasonable request but it looks like you cant do it at this time. What a Total Failure. lol
Update
You can now use
textAllCaps
to force all caps.
What about android:textAllCaps?
By using AppCompat textAllCaps in Android Apps supporting older API's (less than 14)
There is one UI widgets that ships with AppCompat named CompatTextView is a Custom TextView extension that adds support for textAllCaps
For newer android API > 14 you can use :
android:textAllCaps="true"
A simple example:
<android.support.v7.internal.widget.CompatTextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:textAllCaps="true"/>
Source:developer.android
Update:
As it so happens CompatTextView was replaced by AppCompatTextView in
latest appcompat-v7 library ~ Eugen Pechanec
It is really very disappointing that you can't do it with styles (<item name="android:textAllCaps">true</item>) or on each XML layout file with the textAllCaps attribute, and the only way to do it is actually using theString.toUpperCase() on each of the strings when you do a textViewXXX.setText(theString).
In my case, I did not wanted to have theString.toUpperCase() everywhere in my code but to have a centralized place to do it because I had some Activities and lists items layouts with TextViews that where supposed to be capitalized all the time (a title) and other who did not... so... some people may think is an overkill, but I created my own CapitalizedTextView class extending android.widget.TextView and overrode the setText method capitalizing the text on the fly.
At least, if the design changes or I need to remove the capitalized text in future versions, I just need to change to normal TextView in the layout files.
Now, take in consideration that I did this because the App's Designer actually wanted this text (the titles) in CAPS all over the App no matter the original content capitalization, and also I had other normal TextViews where the capitalization came with the the actual content.
This is the class:
package com.realactionsoft.android.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewTreeObserver;
import android.widget.TextView;
public class CapitalizedTextView extends TextView implements ViewTreeObserver.OnPreDrawListener {
public CapitalizedTextView(Context context) {
super(context);
}
public CapitalizedTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CapitalizedTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void setText(CharSequence text, BufferType type) {
super.setText(text.toString().toUpperCase(), type);
}
}
And whenever you need to use it, just declare it with all the package in the XML layout:
<com.realactionsoft.android.widget.CapitalizedTextView
android:id="#+id/text_view_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Some will argue that the correct way to style text on a TextView is to use a SpannableString, but I think that would be even a greater overkill, not to mention more resource-consuming because you'll be instantiating another class than TextView.
I've come up with a solution which is similar with RacZo's in the fact that I've also created a subclass of TextView which handles making the text upper-case.
The difference is that instead of overriding one of the setText() methods, I've used a similar approach to what the TextView actually does on API 14+ (which is in my point of view a cleaner solution).
If you look into the source, you'll see the implementation of setAllCaps():
public void setAllCaps(boolean allCaps) {
if (allCaps) {
setTransformationMethod(new AllCapsTransformationMethod(getContext()));
} else {
setTransformationMethod(null);
}
}
The AllCapsTransformationMethod class is not (currently) public, but still, the source is also available. I've simplified that class a bit (removed the setLengthChangesAllowed() method), so the complete solution is this:
public class UpperCaseTextView extends TextView {
public UpperCaseTextView(Context context) {
super(context);
setTransformationMethod(upperCaseTransformation);
}
public UpperCaseTextView(Context context, AttributeSet attrs) {
super(context, attrs);
setTransformationMethod(upperCaseTransformation);
}
public UpperCaseTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setTransformationMethod(upperCaseTransformation);
}
private final TransformationMethod upperCaseTransformation =
new TransformationMethod() {
private final Locale locale = getResources().getConfiguration().locale;
#Override
public CharSequence getTransformation(CharSequence source, View view) {
return source != null ? source.toString().toUpperCase(locale) : null;
}
#Override
public void onFocusChanged(View view, CharSequence sourceText,
boolean focused, int direction, Rect previouslyFocusedRect) {}
};
}
Basically, write this in TextView of XML file:
android:textAllCaps="true"
It seems like there is permission on mobile keypad setting, so the easiest way to do this is:
editText.setFilters(new InputFilter[]{new InputFilter.AllCaps()});
hope this will work
PixlUI project allows you to use textAllCaps in any textview or subclass of textview including:
Button,
EditText
AutoCompleteEditText
Checkbox
RadioButton
and several others.
You will need to create your textviews using the pixlui version rather than the ones from the android source, meaning you have to do this:
<com.neopixl.pixlui.components.textview.TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world"
pixlui:textAllCaps="true" />
PixlUI also allows you to set a custom typeface/font which you put in your assets folder.
I'm working on a Gradle fork of the PixlUI framework which uses gradle and allows one to specify textAllCaps as well as the typeface from styles rather than requiring them inline as the original project does.

Categories

Resources