I am trying to use TextViewEx, (Textjustify) from here
[link] (https://github.com/bluejamesbond/TextJustify-Android )
to get text justification effect in my project but if I copy and paste the files directly to my project then the files are giving error like some thing (other files) are missing. Also I have searched for how to use TextViewEx but I got is this result below
[link] (TextViewEx, (Textjustify))
In this some one told to import the files to the root folder. What does it mean (the root folder). Also if any one has sample code that uses TextViewEx or any other easy way to justify text in Android or sample code that shows justification of text then plz help me Thanks.
Well I have struggled a lot but could not found any help to solve this problem but I have found another alternative to justify text. Use this class if one having problem with justification of text.
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
public class JustifiedTextView extends WebView {
private String core = "<html><body style='text-align:justify;color:rgba(%s);font-size:%dpx;margin: 0px 0px 0px 0px;'>%s</body></html>";
private String text;
private int textColor;
private int backgroundColor;
private int textSize;
public JustifiedTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public JustifiedTextView(Context context, AttributeSet attrs, int i) {
super(context, attrs, i);
init(attrs);
}
#SuppressWarnings("deprecation")
#SuppressLint("NewApi")
public JustifiedTextView(Context context, AttributeSet attrs, int i,
boolean b) {
super(context, attrs, i, b);
init(attrs);
}
private void init(AttributeSet attrs) {
TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.JustifiedTextView);
text = a.getString(R.styleable.JustifiedTextView_text);
if (text == null)
text = "";
textColor = a.getColor(R.styleable.JustifiedTextView_textColor,
Color.BLACK);
backgroundColor = a.getColor(
R.styleable.JustifiedTextView_backgroundColor,
Color.TRANSPARENT);
textSize = a.getInt(R.styleable.JustifiedTextView_textSize, 12);
a.recycle();
this.setWebChromeClient(new WebChromeClient() {
});
reloadData();
}
public void setText(String s) {
if (s == null)
this.text = "";
else
this.text = s;
reloadData();
}
#SuppressLint("NewApi")
private void reloadData() {
if (text != null) {
String data = String
.format(core, toRgba(textColor), textSize, text);
Log.d("test", data);
this.loadDataWithBaseURL(null, data, "text/html", "utf-8", null);
}
// set WebView's background color *after* data was loaded.
super.setBackgroundColor(backgroundColor);
// Hardware rendering breaks background color to work as expected.
// Need to use software renderer in that case.
if (android.os.Build.VERSION.SDK_INT >= 11)
this.setLayerType(WebView.LAYER_TYPE_SOFTWARE, null);
}
public void setTextColor(int hex) {
textColor = hex;
reloadData();
}
public void setBackgroundColor(int hex) {
backgroundColor = hex;
reloadData();
}
public void setTextSize(int textSize) {
this.textSize = textSize;
reloadData();
}
#SuppressLint("DefaultLocale")
private String toRgba(int hex) {
String h = Integer.toHexString(hex);
int a = Integer.parseInt(h.substring(0, 2), 16);
int r = Integer.parseInt(h.substring(2, 4), 16);
int g = Integer.parseInt(h.substring(4, 6), 16);
int b = Integer.parseInt(h.substring(6, 8), 16);
return String.format("%d,%d,%d,%d", r, g, b, a);
}
}
this is the attrib.xml class
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="JustifiedTextView">
<attr name="text" format="string" localization="suggested" />
<attr name="textColor" format="color|reference" />
<attr name="backgroundColor" format="color|reference" />
<attr name="textSize" format="integer" min="1" />
</declare-styleable>
</resources>
In the layout class use this as
<com.example.animationtest.JustifiedTextView
android:id="#+id/tjTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
app:text="Your Text Here" >
</com.example.animationtest.JustifiedTextView>
com.example.animationtest is the package name in your project where you put the file JustifiedTextView.class
In the code behind class access to the control as
JustifiedTextView tjTextView;
tvTextView2.setTextSize(convertToDp(24));
tjTextView.setTextColor(Color.RED);
tjTextView.setTextSize((int) convertFromDp(18));
where convertFromDp is used to get text size according to screen.
public float convertFromDp(int input) {
final float scale = getResources().getDisplayMetrics().density;
return ((input - 0.5f) / scale);
}
Related
I'm trying to make a custom EditText for currency which means I need to have a prefix of it for the currency and I have to limit users' input to numbers only.
This is my custom EditText code
public OpenSansEditText(Context context, AttributeSet attrs) {
super(context, attrs);
paint = getPaint();
applyCustomFont(context, attrs);
}
public OpenSansEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
paint = getPaint();
applyCustomFont(context, attrs);
}
private void applyCustomFont(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.OpenSansET);
...
// Prefix
String prefix = a.getString(R.styleable.OpenSansET_prefix);
if (prefix != null) {
mPrefix = prefix;
} else {
mPrefix = "";
}
// Prefix Color
int prefixColor = a.getColor(R.styleable.OpenSansET_prefixColor, 0);
if (prefix != null) {
mPrefixColor = prefixColor;
} else {
mPrefixColor = ContextCompat.getColor(context, R.color.miBlack);
}
a.recycle();
}
...
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!mPrefix.equals("")) {
getPaint().getTextBounds(mPrefix, 0, mPrefix.length(), mPrefixRect);
mPrefixRect.right += getPaint().measureText(" "); // add some offset
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!mPrefix.equals("")) {
paint.setColor(mPrefixColor);
canvas.drawText(mPrefix, super.getCompoundPaddingLeft(), getBaseline(), paint);
}
}
#Override
public int getCompoundPaddingLeft() {
return mPrefix.equals("") ? super.getCompoundPaddingLeft()
: super.getCompoundPaddingLeft() + mPrefixRect.width();
}
This is how I use it in xml :
<com.asta.www.classes.OpenSansEditText
android:id="#+id/shopping_filter_priceMinRange"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.4"
android:gravity="center"
android:hint="#string/min"
android:textColor="#color/miBlack"
android:textColorHint="#color/miGrey"
app:prefix="$"
app:prefixColor="#color/miBlack" />
<com.asta.www.classes.OpenSansEditText
android:id="#+id/shopping_filter_priceMaxRange"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.4"
android:gravity="center"
android:hint="#string/max"
android:inputType="number"
android:textColorHint="#color/miGrey"
app:prefix="$"
app:prefixColor="#color/miBlack" />
Which yields :
Only the first one without inputType as number has the currency sign shown, whereas the second ET doesn't have its currency sign shown.
How to achieve currency prefix as text and still keeping inputType to numbers only for user? And I don't want to use two views, namely EditText and TextView to left of it, both inside a ViewGroup to achieve that.
For this type of scenarios I use Compound views. Please see below code for more information.
First create a layout for your custom view like below.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/txt_prefix"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="$" />
<EditText
android:id="#+id/et_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="number" />
</LinearLayout>
Now create a new class which should extends the LinearLayout. See below code.
public class OpenSansEditText extends LinearLayout {
private TextView txtPrefix;
private EditText etValue;
private String prefix = "$";
private int prefixColor = Color.BLACK;
public OpenSansEditText(Context context) {
super(context);
initializeViews(context, null);
}
public OpenSansEditText(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
initializeViews(context, attrs);
}
public OpenSansEditText(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializeViews(context, attrs);
}
private void initializeViews(Context context, AttributeSet attrs) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.opensansedittext_view, this,true);
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.OpenSansEditText);
prefix = a.getString(R.styleable.OpenSansEditText_prefix);
prefixColor = a.getColor(R.styleable.OpenSansEditText_prefixColor, Color.BLACK);
}
}
public CharSequence getValue(){
return etValue.getText();
}
public CharSequence getPrefix(){
return txtPrefix.getText();
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
txtPrefix = (TextView) findViewById(R.id.txt_prefix);
etValue = (EditText) findViewById(R.id.et_value);
txtPrefix.setText(prefix);
txtPrefix.setTextColor(prefixColor);
}
}
And then add your attributes to attribute xml file Ex: (attrs.xml in my case)
<resources>
<declare-styleable name="OpenSansEditText">
<attr name="prefix" format="string"/>
<attr name="prefixColor" format="color"/>
</declare-styleable>
</resources>
Now you can use it anywhere in the project as below
<com.asta.www.classes.OpenSansEditText
android:layout_width="match_parent"
android:layout_height="match_parent"
app:prefix="$"
app:prefixColor="#f00"/>
Hope this will help you to solve your problem. Thanks...
In the end I found this link https://gist.github.com/kennydude/5407963 which helps me in the right direction. So what it does is I think making the prefix as Drawable using this class :
private class TagDrawable extends Drawable {
public String text = "";
public void setText(String s){
text = s;
// Tell it we need to be as big as we want to be!
setBounds(0,0,getIntrinsicWidth(),getIntrinsicHeight());
invalidateSelf();
}
#Override
public void draw(#NonNull Canvas canvas) {
// I don't know why this y works here, but it does :)
// (aka if you are from Google/are Jake Wharton and I have done it wrong, please tell me!)
canvas.drawText( text, 0, mLine0Baseline + canvas.getClipBounds().top, mTextPaint );
}
#Override public void setAlpha(int i) {}
#Override public void setColorFilter(ColorFilter colorFilter) {}
#Override public int getOpacity() {return PixelFormat.UNKNOWN;}
#Override public int getIntrinsicHeight (){
return (int)mFontHeight;
}
#Override public int getIntrinsicWidth(){
return (int)mTextPaint.measureText( text );
}
}
And draw it to the left of the TextView like
TagDrawable left = new TagDrawable();
left.setText("$");
setCompoundDrawablesRelative(left, null, null, null);
The link I supplied even has suffix support which I haven't tried.
As you can see in the image above. There are three views on this screenshot.
- The first item is CheckBox with text and having state off.
- The second item is CheckBox without text and having state on.
- The last item is ImageView with src pointing to the drawable image.
The CheckBoxes were customized using android:button.
As I tried using smaller images, all of the checkbox is left-aligned.
Comparing these two images tell me that the default size of the CheckBox seems fixed to certain size until text attribute is large enough to require extending.
There is nothing special in the file as well. See following.
custom_cb.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/creditcard_selected" android:state_checked="true" />
<item android:drawable="#drawable/creditcard"/>
</selector>
layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<CheckBox android:id="#+id/cbFalse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:button="#drawable/custom_cb"
android:text="" />
<CheckBox android:id="#+id/cbTrue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:button="#drawable/custom_cb"
android:focusable="false"
android:checked="true"
android:layout_toRightOf="#id/cbFalse" />
<ImageView android:id="#+id/imvTrue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/creditcard"
android:layout_toRightOf="#id/cbTrue" />
</RelativeLayout>
Is there anyway I can use bigger image for CheckBox while keeping the size as wrap_content? If I set CheckBox layout_width to actual pixel or dp then it display full image but that mean I have to manually check for the size every time it change.
Today I had the same problem (my custom image was cutted on the left side).
I fixed it putting:
android:button="#null"
android:background="#drawable/my_custom_checkbox_state.xml"
Just use
android:button="#null"
android:background="#null"
android:drawableLeft="your custom selector"
android:drawablePadding="as you need"
android:text="your text"
Thats it.. Its working fine..
You only have to change your drawable from android:button to android:drawableLeft or android:drawableRight. And set the button to null to not show the default checkbox.
My checkbox looks like this:
<CheckBox
android:id="#+id/cb_toggle_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:button="#null"
android:drawableLeft="#drawable/livescore_btn_check" />
CheckBox drawables did not work for me at all, android:button and android:background gave completely erratic results and nothing could fix it.
So I wrote my own "custom checkbox".
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import org.apache.commons.lang3.StringUtils;
import butterknife.Bind;
import butterknife.ButterKnife;
import com.example.myapp.R;
/**
* Created by Zhuinden on 2015.12.02..
*/
public class CustomCheckbox
extends LinearLayout
implements View.OnClickListener {
public CustomCheckbox(Context context) {
super(context);
init(null, -1);
}
public CustomCheckbox(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, -1);
}
#TargetApi(11)
public CustomCheckbox(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs, defStyleAttr);
}
#TargetApi(21)
public CustomCheckbox(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs, defStyleAttr);
}
private void init(AttributeSet attributeSet, int defStyle) {
TypedArray a = null;
if(defStyle != -1) {
a = getContext().obtainStyledAttributes(attributeSet, R.styleable.CustomCheckbox, defStyle, 0);
} else {
a = getContext().obtainStyledAttributes(attributeSet, R.styleable.CustomCheckbox);
}
defImageRes = a.getResourceId(0, 0);
checkedImageRes = a.getResourceId(1, 0);
checked = a.getBoolean(2, false);
typeface = a.getString(3);
if(StringUtils.isEmpty(typeface)) {
typeface = "Oswald-Book.otf";
}
text = a.getString(4);
inactiveTextcolor = a.getInteger(5, android.R.color.black);
activeTextcolor = a.getInteger(6, android.R.color.red);
textsize = a.getDimensionPixelSize(7, 0);
a.recycle();
setOnClickListener(this);
if(!isInEditMode()) {
LayoutInflater.from(getContext()).inflate(R.layout.view_custom_checkbox, this, true);
ButterKnife.bind(this);
imageView.setImageResource(checked ? checkedImageRes : defImageRes);
typefaceTextView.setTypeface(typeface);
if(!StringUtils.isEmpty(text)) {
typefaceTextView.setText(text);
}
if(textsize != 0) {
typefaceTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textsize);
} else {
typefaceTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12);
}
}
}
boolean checked;
int defImageRes;
int checkedImageRes;
String typeface;
String text;
int inactiveTextcolor;
int activeTextcolor;
int textsize;
OnCheckedChangeListener onCheckedChangeListener;
#Bind(R.id.custom_checkbox_imageview)
ImageView imageView;
#Bind(R.id.custom_checkbox_text)
TypefaceTextView typefaceTextView;
#Override
protected void onFinishInflate() {
super.onFinishInflate();
}
#Override
public void onClick(View v) {
checked = !checked;
imageView.setImageResource(checked ? checkedImageRes : defImageRes);
typefaceTextView.setTextColor(checked ? activeTextcolor : inactiveTextcolor);
onCheckedChangeListener.onCheckedChanged(this, checked);
}
public void setOnCheckedChangeListener(OnCheckedChangeListener onCheckedChangeListener) {
this.onCheckedChangeListener = onCheckedChangeListener;
}
public static interface OnCheckedChangeListener {
void onCheckedChanged(View buttonView, boolean isChecked);
}
public boolean isChecked() {
return checked;
}
public void setChecked(boolean checked) {
this.checked = checked;
imageView.setImageResource(checked ? checkedImageRes : defImageRes);
typefaceTextView.setTextColor(checked ? activeTextcolor : inactiveTextcolor);
}
public void setTextColor(int color) {
typefaceTextView.setTextColor(color);
}
#Override
public Parcelable onSaveInstanceState() {
//begin boilerplate code that allows parent classes to save state
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
//end
ss.checked = this.checked;
ss.defImageRes = this.defImageRes;
ss.checkedImageRes = this.checkedImageRes;
ss.typeface = this.typeface;
ss.text = this.text;
ss.inactiveTextcolor = this.inactiveTextcolor;
ss.activeTextcolor = this.activeTextcolor;
ss.textsize = this.textsize;
return ss;
}
#Override
public void onRestoreInstanceState(Parcelable state) {
//begin boilerplate code so parent classes can restore state
if(!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
//end
this.checked = ss.checked;
this.defImageRes = ss.defImageRes;
this.checkedImageRes = ss.checkedImageRes;
this.typeface = ss.typeface;
this.text = ss.text;
this.inactiveTextcolor = ss.inactiveTextcolor;
this.activeTextcolor = ss.activeTextcolor;
this.textsize = ss.textsize;
}
static class SavedState
extends BaseSavedState {
boolean checked;
int defImageRes;
int checkedImageRes;
String typeface;
String text;
int inactiveTextcolor;
int activeTextcolor;
int textsize;
SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
this.checked = in.readByte() > 0;
this.defImageRes = in.readInt();
this.checkedImageRes = in.readInt();
this.typeface = in.readString();
this.text = in.readString();
this.inactiveTextcolor = in.readInt();
this.activeTextcolor = in.readInt();
this.textsize = in.readInt();
}
#Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeByte(this.checked ? (byte) 0x01 : (byte) 0x00);
out.writeInt(this.defImageRes);
out.writeInt(this.checkedImageRes);
out.writeString(this.typeface);
out.writeString(this.text);
out.writeInt(this.inactiveTextcolor);
out.writeInt(this.activeTextcolor);
out.writeInt(this.textsize);
}
//required field that makes Parcelables from a Parcel
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}
Using the following attrs.xml
<resources
<declare-styleable name="CustomCheckbox">
<attr name="default_img" format="integer"/>
<attr name="checked_img" format="integer"/>
<attr name="checked" format="boolean"/>
<attr name="chx_typeface" format="string"/>
<attr name="text" format="string"/>
<attr name="inactive_textcolor" format="integer"/>
<attr name="active_textcolor" format="integer"/>
<attr name="textsize" format="dimension"/>
</declare-styleable>
</resources>
With following view_custom_checkbox.xml layout:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:id="#+id/custom_checkbox_imageview"
android:layout_width="#dimen/_15sdp"
android:layout_height="#dimen/_15sdp"
/>
<com.example.TypefaceTextView
android:id="#+id/custom_checkbox_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</merge>
And example:
<com.example.CustomCheckbox
android:id="#+id/program_info_record_button"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:clickable="true"
android:gravity="center"
app:default_img="#drawable/ic_recording_off"
app:checked_img="#drawable/ic_recording_on"
app:text="#string/record"
app:inactive_textcolor="#color/program_info_buttons_inactive"
app:active_textcolor="#color/active_color"
app:textsize="#dimen/programInfoButtonTextSize"
app:chx_typeface="SomeTypeface.otf"/>
Modify where necessary.
Try using a Linearlayout with horizontal orientation instead of RelativeLayout.Also use weight in every layout to force views to use same width.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<CheckBox android:id="#+id/cbFalse"
android:weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:button="#drawable/custom_cb"
android:text="" />
<CheckBox android:id="#+id/cbTrue"
android:weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:button="#drawable/custom_cb"
android:focusable="false"
android:checked="true"
android:layout_toRightOf="#id/cbFalse" />
<ImageView android:id="#+id/imvTrue"
android:weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="#drawable/creditcard"
android:layout_toRightOf="#id/cbTrue" />
Maybe it will be useful to someone, setting gravity attribute to center helped me to avoid image cropping:
<CheckBox
android:id="#+id/cb_accept_agreement"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:button="#drawable/checkbox_selector"/>
I am new to Android and I am still searching for good resources to mine.
My question involves the best way to draw a grid of rectangle shaped objects to the screen. I need each object displayed in the grid to have an initial text (or an int as text) value, and when a user touches that object, the text will change to a colored shape. Also, each object needs to be aware of (or be able to find out) the state of its immediate neighbors.
I don't know which class to extend in order to be able to have both text and shapes display on it, and be able to handle touch input.
Thank you for the help.
Edit:
I apologize, but I don't know how to be more clear. Perhaps some context will help. I have a main activity that takes an int value as input and creates an Intent which sends that value to another activity. That other activity then displays a grid of 100 random numbers. The user needs to select a series of grid spots and a certain number of those spots that the user chose will change from the random number to a colored shape. The spots that change are controlled by logic that I will provide in the code.
SO Q with basic android graph: Custom dynamic graph in Android
Android official guide on custom controls
https://developer.android.com/guide/topics/ui/custom-components.html
Android reference page on the View control, which you'll be expanding on
https://developer.android.com/reference/android/view/View.html
real world code example of a custom View control (you're particularly interested in onDraw() )
http://www.java2s.com/Open-Source/Android/App/ringdroid/com/ringdroid/WaveformView.java.htm
I know its a long time after the question has been asked but maybe it will help ppl :)
Add this to "attrs.xml" (or create new if needed)
<resources>
<declare-styleable name="RectanglesGridView">
<attr name="cellSize" format="dimension" />
<attr name="cellColor1" format="color" />
<attr name="cellColor2" format="color" />
</declare-styleable>
And this is the class - "RectanglesGridView.java"
package com.gilapps.movinglivewallpaper.UI.views;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Build;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import com.gilapps.movinglivewallpaper.R;
public class RectanglesGridView extends View {
private final static float DEFAULT_CELL_SIZE_DP = 10;
private final static int DEFAULT_CELL_COLOR1 = Color.GRAY;
private final static int DEFAULT_CELL_COLOR2 = Color.WHITE;
private int mColor1 = DEFAULT_CELL_COLOR1;
private int mColor2 = DEFAULT_CELL_COLOR2;
private float mCellSize;
private Paint mPaint;
private boolean mIsColor1;
private int mWidth;
private int mHeight;
public RectanglesGridView(Context context) {
super(context);
mCellSize = convertDpToPixel(DEFAULT_CELL_SIZE_DP);
mPaint = new Paint();
}
public RectanglesGridView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
loadAttributes(context, attrs);
}
public RectanglesGridView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
loadAttributes(context, attrs);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public RectanglesGridView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mPaint = new Paint();
loadAttributes(context, attrs);
}
private void loadAttributes(Context context, AttributeSet attrs) {
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.RectanglesGridView,
0, 0);
try {
mCellSize = a.getDimension(R.styleable.RectanglesGridView_cellSize, convertDpToPixel(DEFAULT_CELL_SIZE_DP));
mColor1 = a.getColor(R.styleable.RectanglesGridView_cellColor1, DEFAULT_CELL_COLOR1);
mColor2 = a.getColor(R.styleable.RectanglesGridView_cellColor2, DEFAULT_CELL_COLOR2);
} catch (Exception e) {
mCellSize = convertDpToPixel(DEFAULT_CELL_SIZE_DP);
} finally {
a.recycle();
}
}
private float convertDpToPixel(float dp){
Resources resources = getContext().getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
float px = dp * ((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
return px;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mWidth = w;
mHeight = h;
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
for (float r=0;r<mHeight;r+=mCellSize) {
for (float c=0;c<mWidth;c+=mCellSize) {
mPaint.setColor(mIsColor1 ? mColor2 : mColor1);
mIsColor1 = !mIsColor1;
canvas.drawRect(c,r,c+mCellSize,r+mCellSize,mPaint);
}
mIsColor1 = !mIsColor1;
}
super.onDraw(canvas);
}
}
Usage:
<com.gilapps.movinglivewallpaper.UI.views.RectanglesGridView
app:cellColor1="#33000000"
app:cellColor2="white"
app:cellSize="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
add xmlns:app="http://schemas.android.com/apk/res-auto" to the root view
Change the package name if needed
Enjoy! :)
I'm looking for a simple way to forget that I'm using a WebView to have justified text in my TextView. Has someone made a custom view for this? I'm well aware that I can do something like this:
WebView view = new WebView(this);
view.loadData("my html with text justification","text/html","utf-8");
But it gets ugly when you want to set the size, the color or other common properties of the TextView. There must be a more convenient way of doing it.
It was getting on my nerves, I admit it. I like the TextViews to look like TextViews in the code, and even if I'm using a WebView as the means of achieving the text-align:justified formatting, I don't want to look at it that way.
I created a custom view (ugly, probably bad) that implements the methods that I commonly use from the TextView and modifies the content of the WebView in order to reflect those changes.
Wether it's useful for someone else or a potential hazard I really don't know, for me it works, I've used it in several projects and haven't run into any issues. The only minor inconvenience is that I assume it as a bigger toll memory-wise but nothing to worry about if it's just one or two (correct me if I'm wrong).
The result is the following:
And the code for setting it programmatically is as simple as this:
JustifiedTextView J = new JustifiedTextView();
J.setText("insert your text here");
Of course it'd be stupid to leave it like that so I also added the methods for changing the font-size and the font-color which are basically all I use TextViews for. Meaning I can do something like this:
JustifiedTextView J = new JustifiedTextView();
J.setText("insert your text here");
J.setTextColor(Color.RED);
J.setTextSize(30);
And obtain the following result (images are cropped):
But, this is not to show us how it looks, it's to share how you've done it!
I know, I know.
Here's the full code. It also addresses Problems when setting transparent background and loading UTF-8 strings into the view. See the comments in reloadData() for details.
public class JustifiedTextView extends WebView{
private String core = "<html><body style='text-align:justify;color:rgba(%s);font-size:%dpx;margin: 0px 0px 0px 0px;'>%s</body></html>";
private String textColor = "0,0,0,255";
private String text = "";
private int textSize = 12;
private int backgroundColor=Color.TRANSPARENT;
public JustifiedTextView(Context context, AttributeSet attrs) {
super(context, attrs);
this.setWebChromeClient(new WebChromeClient(){});
}
public void setText(String s){
this.text = s;
reloadData();
}
#SuppressLint("NewApi")
private void reloadData(){
// loadData(...) has a bug showing utf-8 correctly. That's why we need to set it first.
this.getSettings().setDefaultTextEncodingName("utf-8");
this.loadData(String.format(core,textColor,textSize,text), "text/html","utf-8");
// set WebView's background color *after* data was loaded.
super.setBackgroundColor(backgroundColor);
// Hardware rendering breaks background color to work as expected.
// Need to use software renderer in that case.
if(android.os.Build.VERSION.SDK_INT >= 11)
this.setLayerType(WebView.LAYER_TYPE_SOFTWARE, null);
}
public void setTextColor(int hex){
String h = Integer.toHexString(hex);
int a = Integer.parseInt(h.substring(0, 2),16);
int r = Integer.parseInt(h.substring(2, 4),16);
int g = Integer.parseInt(h.substring(4, 6),16);
int b = Integer.parseInt(h.substring(6, 8),16);
textColor = String.format("%d,%d,%d,%d", r, g, b, a);
reloadData();
}
public void setBackgroundColor(int hex){
backgroundColor = hex;
reloadData();
}
public void setTextSize(int textSize){
this.textSize = textSize;
reloadData();
}
}
In just three steps, you can justify your web view text.
1)
// Justify tag
String justifyTag = "<html><body style='text-align:justify;'>%s</body></html>";
2)
// Concatenate your string with the tag to Justify it
String dataString = String.format(Locale.US, justifyTag, "my html with text justification");
3)
// Load the data in the web view
webView.loadDataWithBaseURL("", dataString, "text/html", "UTF-8", "");
Without webview solution is : https://github.com/merterhk/JustifiedTextView
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.text.TextPaint;
import android.view.View;
public class JustifiedTextView extends View {
String text;
ArrayList<Line> linesCollection = new ArrayList<Line>();
TextPaint textPaint;
Typeface font;
int textColor;
float textSize = 42f, lineHeight = 57f, wordSpacing = 15f, lineSpacing = 15f;
float onBirim, w, h;
float leftPadding, rightPadding;
public JustifiedTextView(Context context, String text) {
super(context);
this.text = text;
init();
}
private void init() {
textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
textColor = Color.BLACK;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (font != null) {
font = Typeface.createFromAsset(getContext().getAssets(), "font/Trykker-Regular.ttf");
textPaint.setTypeface(font);
}
textPaint.setColor(textColor);
int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();
w = resolveSizeAndState(minw, widthMeasureSpec, 1);
h = MeasureSpec.getSize(widthMeasureSpec);
onBirim = 0.009259259f * w;
lineHeight = textSize + lineSpacing;
leftPadding = 3 * onBirim + getPaddingLeft();
rightPadding = 3 * onBirim + getPaddingRight();
textPaint.setTextSize(textSize);
wordSpacing = 15f;
Line lineBuffer = new Line();
this.linesCollection.clear();
String[] lines = text.split("\n");
for (String line : lines) {
String[] words = line.split(" ");
lineBuffer = new Line();
float lineWidth = leftPadding + rightPadding;
float totalWordWidth = 0;
for (String word : words) {
float ww = textPaint.measureText(word) + wordSpacing;
if (lineWidth + ww + (lineBuffer.getWords().size() * wordSpacing) > w) {// is
lineBuffer.addWord(word);
totalWordWidth += textPaint.measureText(word);
lineBuffer.setSpacing((w - totalWordWidth - leftPadding - rightPadding) / (lineBuffer.getWords().size() - 1));
this.linesCollection.add(lineBuffer);
lineBuffer = new Line();
totalWordWidth = 0;
lineWidth = leftPadding + rightPadding;
} else {
lineBuffer.setSpacing(wordSpacing);
lineBuffer.addWord(word);
totalWordWidth += textPaint.measureText(word);
lineWidth += ww;
}
}
this.linesCollection.add(lineBuffer);
}
setMeasuredDimension((int) w, (int) ((this.linesCollection.size() + 1) * lineHeight + (10 * onBirim)));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawLine(0f, 10f, getMeasuredWidth(), 10f, textPaint);
float x, y = lineHeight + onBirim;
for (Line line : linesCollection) {
x = leftPadding;
for (String s : line.getWords()) {
canvas.drawText(s, x, y, textPaint);
x += textPaint.measureText(s) + line.spacing;
}
y += lineHeight;
}
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Typeface getFont() {
return font;
}
public void setFont(Typeface font) {
this.font = font;
}
public float getLineHeight() {
return lineHeight;
}
public void setLineHeight(float lineHeight) {
this.lineHeight = lineHeight;
}
public float getLeftPadding() {
return leftPadding;
}
public void setLeftPadding(float leftPadding) {
this.leftPadding = leftPadding;
}
public float getRightPadding() {
return rightPadding;
}
public void setRightPadding(float rightPadding) {
this.rightPadding = rightPadding;
}
public void setWordSpacing(float wordSpacing) {
this.wordSpacing = wordSpacing;
}
public float getWordSpacing() {
return wordSpacing;
}
public float getLineSpacing() {
return lineSpacing;
}
public void setLineSpacing(float lineSpacing) {
this.lineSpacing = lineSpacing;
}
class Line {
ArrayList<String> words = new ArrayList<String>();
float spacing = 15f;
public Line() {
}
public Line(ArrayList<String> words, float spacing) {
this.words = words;
this.spacing = spacing;
}
public void setSpacing(float spacing) {
this.spacing = spacing;
}
public float getSpacing() {
return spacing;
}
public void addWord(String s) {
words.add(s);
}
public ArrayList<String> getWords() {
return words;
}
}
}
This is the same JustifiedTextView class given by Juan (and edited by me), but extended to work with custom xml attributes you can use in your layout xml files. Even Eclipse layout editor will show your custom attributes in the attribute table, which is cool. I put this into an additional answer, in case you want to keep things clean and don't need xml attributes.
public class JustifiedTextView extends WebView{
private String core = "<html><body style='text-align:justify;color:rgba(%s);font-size:%dpx;margin: 0px 0px 0px 0px;'>%s</body></html>";
private String text;
private int textColor;
private int backgroundColor;
private int textSize;
public JustifiedTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public JustifiedTextView(Context context, AttributeSet attrs, int i) {
super(context, attrs, i);
init(attrs);
}
#SuppressLint("NewApi")
public JustifiedTextView(Context context, AttributeSet attrs, int i, boolean b) {
super(context, attrs, i, b);
init(attrs);
}
private void init(AttributeSet attrs) {
TypedArray a=getContext().obtainStyledAttributes(
attrs,
R.styleable.JustifiedTextView);
text = a.getString(R.styleable.JustifiedTextView_text);
if(text==null) text="";
textColor = a.getColor(R.styleable.JustifiedTextView_textColor, Color.BLACK);
backgroundColor = a.getColor(R.styleable.JustifiedTextView_backgroundColor, Color.TRANSPARENT);
textSize = a.getInt(R.styleable.JustifiedTextView_textSize, 12);
a.recycle();
this.setWebChromeClient(new WebChromeClient(){});
reloadData();
}
public void setText(String s){
if(s==null)
this.text="";
else
this.text = s;
reloadData();
}
#SuppressLint("NewApi")
private void reloadData(){
if(text!=null) {
String data = String.format(core,toRgba(textColor),textSize,text);
Log.d("test", data);
this.loadDataWithBaseURL(null, data, "text/html","utf-8", null);
}
// set WebView's background color *after* data was loaded.
super.setBackgroundColor(backgroundColor);
// Hardware rendering breaks background color to work as expected.
// Need to use software renderer in that case.
if(android.os.Build.VERSION.SDK_INT >= 11)
this.setLayerType(WebView.LAYER_TYPE_SOFTWARE, null);
}
public void setTextColor(int hex){
textColor = hex;
reloadData();
}
public void setBackgroundColor(int hex){
backgroundColor = hex;
reloadData();
}
public void setTextSize(int textSize){
this.textSize = textSize;
reloadData();
}
private String toRgba(int hex) {
String h = Integer.toHexString(hex);
int a = Integer.parseInt(h.substring(0, 2),16);
int r = Integer.parseInt(h.substring(2, 4),16);
int g = Integer.parseInt(h.substring(4, 6),16);
int b = Integer.parseInt(h.substring(6, 8),16);
return String.format("%d,%d,%d,%d", r, g, b, a);
}
}
Either add this as justified_text_view_attr.xml into your res/values/ folder, or merge it into your existing attrs.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="JustifiedTextView">
<attr name="text" format="string" localization="suggested"/>
<attr name="textColor" format="color|reference" />
<attr name="backgroundColor" format="color|reference" />
<attr name="textSize" format="integer" min="1" />
</declare-styleable>
</resources>
Feel free to edit if you find any bugs.
I believe this simplest form. And I worked perfectly
package domo.suichbt.util;
import android.content.Context;
import android.text.Html;
import android.util.AttributeSet;
import android.widget.TextView;
public class JustifiedTextView extends TextView
{
private final String CORE_TEMPLATE = "<html><body style='text-
align:justify;margin: 0px 0px 0px 0px;'>%s</body></html>";
public JustifiedTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setText(Html.fromHtml(String.format(CORE_TEMPLATE,getText())));
}
public JustifiedTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setText(Html.fromHtml(String.format(CORE_TEMPLATE,getText())));
}
public JustifiedTextView(Context context) {
super(context);
setText(Html.fromHtml(String.format(CORE_TEMPLATE,getText())));
}
public JustifiedTextView(Context context, AttributeSet attrs) {
super(context, attrs);
setText(Html.fromHtml(String.format(CORE_TEMPLATE,getText())));
}
}
Insert xml example
<domo.suichbt.util.JustifiedTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/sw_titulo"
android:singleLine="false">
</domo.suichbt.util.JustifiedTextView>
Take a look at this link. It uses a WebView to be bale to fully justify the text of a CheckBox in Android. It also can be used exactly the same in TextView, since each CheckBox is in fact a TextView and a Button.
http://www.collegemobile.com/2014/09/justify-text-android-checkbox/
How can i change letter spacing in a textview?
Will it help if I have HTML text in it (I cannot use webview in my code).
P.S. I'm using my own typeface in the textview with HTML text.
Since API 21 there is an option set letter spacing. You can call method setLetterSpacing or set it in XML with attribute letterSpacing.
More space:
android:letterSpacing="0.1"
Less space:
android:letterSpacing="-0.07"
check out android:textScaleX
Depending on how much spacing you need, this might help. That's the only thing remotely related to letter-spacing in the TextView.
Edit: please see #JerabekJakub's response below for an updated, better method to do this starting with api 21 (Lollipop)
This answer is based on Pedro's answer but adjusted so it also works if text attribute is already set:
package nl.raakict.android.spc.widget;
import android.content.Context;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ScaleXSpan;
import android.util.AttributeSet;
import android.widget.TextView;
public class LetterSpacingTextView extends TextView {
private float letterSpacing = LetterSpacing.BIGGEST;
private CharSequence originalText = "";
public LetterSpacingTextView(Context context) {
super(context);
}
public LetterSpacingTextView(Context context, AttributeSet attrs){
super(context, attrs);
originalText = super.getText();
applyLetterSpacing();
this.invalidate();
}
public LetterSpacingTextView(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
}
public float getLetterSpacing() {
return letterSpacing;
}
public void setLetterSpacing(float letterSpacing) {
this.letterSpacing = letterSpacing;
applyLetterSpacing();
}
#Override
public void setText(CharSequence text, BufferType type) {
originalText = text;
applyLetterSpacing();
}
#Override
public CharSequence getText() {
return originalText;
}
private void applyLetterSpacing() {
if (this == null || this.originalText == null) return;
StringBuilder builder = new StringBuilder();
for(int i = 0; i < originalText.length(); i++) {
String c = ""+ originalText.charAt(i);
builder.append(c.toLowerCase());
if(i+1 < originalText.length()) {
builder.append("\u00A0");
}
}
SpannableString finalText = new SpannableString(builder.toString());
if(builder.toString().length() > 1) {
for(int i = 1; i < builder.toString().length(); i+=2) {
finalText.setSpan(new ScaleXSpan((letterSpacing+1)/10), i, i+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
super.setText(finalText, BufferType.SPANNABLE);
}
public class LetterSpacing {
public final static float NORMAL = 0;
public final static float NORMALBIG = (float)0.025;
public final static float BIG = (float)0.05;
public final static float BIGGEST = (float)0.2;
}
}
If you want to use it programatically:
LetterSpacingTextView textView = new LetterSpacingTextView(context);
textView.setSpacing(10); //Or any float. To reset to normal, use 0 or LetterSpacingTextView.Spacing.NORMAL
textView.setText("My text");
//Add the textView in a layout, for instance:
((LinearLayout) findViewById(R.id.myLinearLayout)).addView(textView);
after API >=21 there is inbuild method provided by TextView called setLetterSpacing
check this for more
I built a custom class that extends TextView and solves this problem... Check out my answer here =)
As android doesn't support such a thing, you can do it manually with FontCreator. It has good options for font modifying.
I used this tool to build a custom font, even if it takes some times but you can always use it in your projects.
For embedding HTML text in your textview you can use Html.fromHTML() syntax.
More information you will get from http://developer.android.com/reference/android/text/Html.html#fromHtml%28java.lang.String%29