Android TextView Align text to Left and Right - java

I'm trying to have a TextView that has two texts, one aligned at the left side of the TextView and one at the right side of the TextView.
I referred this [Android TextView Align text to Right and Left (#daemontus) for setting the text.
But how do add a text if I enter the text at right side of the TextView first and then left side of the TextView
1) Input: Enter Button 1 to display Text1
2) Output: Enter Button 2 to display Text2 along with Text1
public void setLeftRightText(TextView view, String left, String right,Enum keysel) {
if(keysel == RSK_KEY) {
SpannableString merged=new SpannableString(left + "\n" + right);
merged.setSpan(
new AlignmentSpan.Standard(Layout.Alignment.ALIGN_NORMAL),
0, left.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE
);
merged.setSpan(
new LineOverlapSpan(),
left.length(), left.length() + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE
);
merged.setSpan(
new AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE),
left.length() + 1, left.length() + 1 + right.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE
);
view.setText(merged);
}
else if (keysel == LSK_KEY){
final String resultText = right + " " + left;
final SpannableString styledResultText = new SpannableString(resultText);
styledResultText.setSpan((new AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE )), left.length() + 2, left.length() + 2 +right.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
styledResultText.setSpan((new AlignmentSpan.Standard(Layout.Alignment.ALIGN_NORMAL )), 0, left.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
view.setText(styledResultText);
}
}
LineOverlapSpan.java
public class LineOverlapSpan implements LineHeightSpan {
public void chooseHeight(final CharSequence text, final int start, final int end, final int spanstartv, final int v, final Paint.FontMetricsInt fm) {
fm.bottom += fm.top;
fm.descent += fm.top;
}
}

You can achieve the desired result in a single text view. Create a view that extends text view and override the onDraw
I've put together a small example. Hope it will give you an idea at-least
public class LeftRightTextView extends AppCompatTextView {
private TextPaint mTextPaint;
private String mLeftText, mRightText;
public LeftRightTextView(Context context) {
this(context, null);
}
public LeftRightTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public void setLeftText(String leftText) {
mLeftText = leftText;
setText(mLeftText);
}
public void setRightText(String rightText) {
mRightText = rightText;
invalidate();
}
public LeftRightTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LeftRightTextView, defStyleAttr, 0);
mLeftText = typedArray.getString(R.styleable.LeftRightTextView_lr_left_text);
mRightText = typedArray.getString(R.styleable.LeftRightTextView_lr_right_text);
typedArray.recycle();
setText(mLeftText);
mTextPaint = new TextPaint();
mTextPaint.setColor(getCurrentTextColor());
}
#Override
protected void onDraw(Canvas canvas) {
//let it draw the left text as usual
super.onDraw(canvas);
//Now draw the right text
int rightEnd = getWidth() - getPaddingRight();
float textWidth = mTextPaint.measureText(mRightText);
canvas.drawText(mRightText, rightEnd - textWidth, mTextPaint.getFontMetrics().descent - mTextPaint.getFontMetrics().ascent, mTextPaint);
}
}
And the attributes
<declare-styleable name="LeftRightTextView">
<attr name="lr_left_text" format="string"/>
<attr name="lr_right_text" format="string"/>
</declare-styleable>
In layout
<me.srs.myapplication.LeftRightTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:lr_left_text="Hello"
app:lr_right_text="World"/>
And this is the result

You need to take two separate textviews to achieve your requirements.
Please use following code and check.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#android:color/black"
android:padding="10dp"
android:weightSum="1">
<TextView
android:text="Left"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:textSize="36sp"
android:fontFamily="sans-serif-light"
android:textColor="#android:color/white"
android:background="#android:color/transparent"
android:gravity="left"
android:padding="5dp"/>
<TextView
android:text="Right"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:textSize="36sp"
android:fontFamily="sans-serif-light"
android:textColor="#android:color/white"
android:background="#android:color/transparent"
android:gravity="right"
android:padding="5dp"/>
</LinearLayout>

You need to create a custom view and override onDraw method of same , to achieve desired functionality.

Related

How to Overcome Aliasing Problem Caused By ConstraintLayout Guidelines?

I am trying to reconcile the following two things:
A) I want a precise, uniform, and clean UI with several identically sized buttons that correspond exactly to the underlying 'grid cells' -- A UI that will look as similar as possible (proportionally to screen size) across as many Android devices as possible.
B) On Android, the screen dimensions (aspect ratio and actual pixel numbers) of the user's device are unknown (to the app) until runtime.
My solution to this was to: (there is a code example below!)
1) Lock the app to portrait mode,
2) Do not define anything in static/absolute terms like dp,px, etc. and instead conceptualize a 'basic unit of measure' that is a function of screen height -- 0.08% in my case -- and base everything off of that.
3) Set horizontal guidelines within a ConstraintLayout whose positions are expressed as a percentage of parent (screen) height.
4) Make all buttons use this 'basic unit' as their height and width by setting their XML layout_constraintDimensionRatio attribute to "1:1" and using the guidelines above (see step 3),
5) Accomplish positioning and dimensions of all views by using constraints to either these guidelines, the parent's bounds, or one additional vertical guideline at 50% of screen width.
The problem is that depending on the pixel height of the screen (whether it happens to be odd or even... or maybe other factors), the dimensions of a view/button, (and thus the paths drawn inside it) constrained between one pair of guidelines does not exactly match those of another view drawn between some other pair... even though the distance between both pairs of guidelines should be the same percentage of parent height. :)
Here is an example showing the Nexus 4 emulator:
At first I thought the problem was simply due to rounding 'error' during Android's dimension calculations, but then why would the view not be square even though they are prescribed the 1:1 ratio attribute?
The only solutions I can think of would be:
A) To do the layout programatically instead of with XML... and set the guideline positions as exact pixel locations instead of percentages, and answer the question, "what is 0.08 x screen height?" myself... making the appropriate corrections to compensate for 'indivisible' screen heights.
B) Override onLayout() in the custom views and "force" their dimensions to be consistent... but then this would defeat the purpose of guidelines. :(
But I'm really hoping there is an easier solution than A or B.
(I know someone is going to suggest GridLayout, but it's not an option, for a few reasons... one of which is that in GridLayout, views inside cells must be set to wrap_content... which means the paths they draw cannot be generated relative to parent at runtime).
Thanks for any other suggestions, though.
Code Example:
I whipped up a simple 'minimal example' below that should be easy to reconstruct in Android Studio. The logs will reveal the issue if it's not immediately apparent.
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:id="#+id/rootView"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.constraint.Guideline
android:id="#+id/guidelineHorizontalTop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.08" />
<android.support.constraint.Guideline
android:id="#+id/guidelineHorizontalBottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.92" />
<android.support.constraint.Guideline
android:id="#+id/guidelineHorizontalCenter1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.38" />
<android.support.constraint.Guideline
android:id="#+id/guidelineHorizontalCenter2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.46" />
<android.support.constraint.Guideline
android:id="#+id/guidelineHorizontalCenter3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.54" />
<android.support.constraint.Guideline
android:id="#+id/guidelineHorizontalCenter4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.62" />
<com.example.boober.stack_aliasingproblem.CustomButton
android:id="#+id/buttonTopLeft"
android:layout_width="0dp"
android:layout_height="0dp"
android:tag="buttonTopLeft"
app:layout_constraintBottom_toTopOf="#+id/guidelineHorizontalTop"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.example.boober.stack_aliasingproblem.CustomButton
android:id="#+id/buttonTopRight"
android:layout_width="0dp"
android:layout_height="0dp"
android:tag="buttonTopRight"
app:layout_constraintBottom_toTopOf="#+id/guidelineHorizontalTop"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.example.boober.stack_aliasingproblem.CustomButton
android:id="#+id/buttonBottomLeft"
android:layout_width="0dp"
android:layout_height="0dp"
android:tag="buttonBottomLeft"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/guidelineHorizontalBottom" />
<com.example.boober.stack_aliasingproblem.CustomButton
android:id="#+id/buttonBottomRight"
android:layout_width="0dp"
android:layout_height="0dp"
android:tag="buttonBottomRight"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#+id/guidelineHorizontalBottom" />
<com.example.boober.stack_aliasingproblem.CustomButton
android:id="#+id/buttonMiddle"
android:layout_width="0dp"
android:layout_height="0dp"
android:tag="buttonMiddle"
app:layout_constraintBottom_toBottomOf="#id/guidelineHorizontalCenter3"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#id/guidelineHorizontalCenter2" />
<com.example.boober.stack_aliasingproblem.CustomButton
android:id="#+id/buttonMiddleTopLeft"
android:layout_width="0dp"
android:layout_height="0dp"
android:tag="buttonMiddleTopLeft"
app:layout_constraintBottom_toBottomOf="#id/guidelineHorizontalCenter2"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintEnd_toStartOf="#id/buttonMiddle"
app:layout_constraintTop_toTopOf="#id/guidelineHorizontalCenter1" />
<com.example.boober.stack_aliasingproblem.CustomButton
android:id="#+id/buttonMiddleTopRight"
android:layout_width="0dp"
android:layout_height="0dp"
android:tag="buttonMiddleTopRight"
app:layout_constraintBottom_toBottomOf="#id/guidelineHorizontalCenter2"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toEndOf="#id/buttonMiddle"
app:layout_constraintTop_toTopOf="#id/guidelineHorizontalCenter1" />
</android.support.constraint.ConstraintLayout>
MainActivity.java:
public class MainActivity extends AppCompatActivity {
CustomButton buttonTopLeft;
CustomButton buttonTopRight;
CustomButton buttonMiddle;
CustomButton buttonMiddleTopLeft;
CustomButton getButtonMiddleTopRight;
CustomButton buttonBottomLeft;
CustomButton buttonBottomRight;
CustomButton[] arrayOfCustomButtons;
ConstraintLayout rootView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonTopLeft = findViewById(R.id.buttonTopLeft);
buttonTopRight = findViewById(R.id.buttonTopRight);
buttonBottomLeft = findViewById(R.id.buttonBottomLeft);
buttonBottomRight = findViewById(R.id.buttonBottomRight);
buttonMiddle = findViewById(R.id.buttonMiddle);
buttonMiddleTopLeft = findViewById(R.id.buttonMiddleTopLeft);
getButtonMiddleTopRight = findViewById(R.id.buttonMiddleTopRight);
arrayOfCustomButtons = new CustomButton[]{buttonTopLeft, buttonTopRight, buttonBottomLeft,
buttonBottomRight, buttonMiddle, buttonMiddleTopLeft, getButtonMiddleTopRight};
rootView = findViewById(R.id.rootView);
for (final CustomButton cb : arrayOfCustomButtons) {
cb.setClickable(true);
cb.post(new Runnable() {
#Override
public void run() {
Log.i("XXX", "width of: " + cb.getTag() + " is: "
+ cb.getMeasuredWidth());
}
});
}
rootView.post(new Runnable() {
#Override
public void run() {
Log.i("XXX", "height of rootView is: " + rootView.getMeasuredHeight());
}
});
}
}
CustomButton.java:
public class CustomButton extends View {
Path myOutlinePath;
Paint myThinPaintBrush;
Paint myThickPaintBrush;
boolean isHighlighted = false;
public CustomButton(Context context) {
super(context);
init();
}
public CustomButton(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CustomButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
float measuredWidth = getMeasuredWidth();
Log.i("XXX", "measured WIDTH Of " + this.getTag() + " is: " + measuredWidth);
Log.i("XXX", "measured HEIGT Of " + this.getTag() + " is: " + getMeasuredHeight());
Log.i("XXX", "\n ");
generateMyOutline(measuredWidth);
myThinPaintBrush.setStrokeWidth(measuredWidth/12);
myThickPaintBrush.setStrokeWidth(measuredWidth/6);
}
private void generateMyOutline(float W) {
Path path = new Path();
path.moveTo(0,0);
path.lineTo(W, 0);
path.lineTo(W, W);
path.lineTo(0, W);
path.lineTo(0,0);
myOutlinePath = path;
}
private void init() {
myOutlinePath = new Path();
myThinPaintBrush = new Paint();
myThinPaintBrush.setAntiAlias(false); // setting this to true does not solve the problem.
myThinPaintBrush.setStyle(Paint.Style.STROKE);
myThinPaintBrush.setStrokeCap(Paint.Cap.ROUND);
myThickPaintBrush = new Paint();
myThickPaintBrush.setAntiAlias(false);
myThickPaintBrush.setStyle(Paint.Style.STROKE);
myThickPaintBrush.setStrokeCap(Paint.Cap.ROUND);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (this.isClickable()) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isHighlighted = true;
invalidate();
break;
case MotionEvent.ACTION_UP:
isHighlighted = false;
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
isHighlighted = false;
invalidate();
break;
}
}
return super.onTouchEvent(event);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(myOutlinePath, myThinPaintBrush);
if (isHighlighted) {
canvas.drawPath(myOutlinePath, myThickPaintBrush);
}
super.onDraw(canvas);
}
}
I would go for the middle ground: Use your XML layout as is and make adjustments programmatically to the guideline positions. The following code converts percentage guidelines to fixed position guidelines by computing a new layout height that is a multiple of 8% of the height of the initial layout.
All sizes are computed correctly except for the bottom squares that tend to be larger. This can be easily corrected based upon your actual requirements (more important to be at the bottom or a certain distance from the other squares, for instance.)
MainActivity.jav
public class MainActivity extends AppCompatActivity {
CustomButton buttonTopLeft;
CustomButton buttonTopRight;
CustomButton buttonMiddle;
CustomButton buttonMiddleTopLeft;
CustomButton getButtonMiddleTopRight;
CustomButton buttonBottomLeft;
CustomButton buttonBottomRight;
CustomButton[] arrayOfCustomButtons;
ConstraintLayout rootView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buttonTopLeft = findViewById(R.id.buttonTopLeft);
buttonTopRight = findViewById(R.id.buttonTopRight);
buttonBottomLeft = findViewById(R.id.buttonBottomLeft);
buttonBottomRight = findViewById(R.id.buttonBottomRight);
buttonMiddle = findViewById(R.id.buttonMiddle);
buttonMiddleTopLeft = findViewById(R.id.buttonMiddleTopLeft);
getButtonMiddleTopRight = findViewById(R.id.buttonMiddleTopRight);
rootView = findViewById(R.id.rootView);
rootView.post(new Runnable() {
#Override
public void run() {
int rootViewHeight = rootView.getMeasuredHeight();
Log.i("XXX", "height of rootView is: " + rootViewHeight);
int segHeight = (int) (rootViewHeight * 0.08f);
adjustGuideline(R.id.guidelineHorizontalTop, segHeight);
adjustGuideline(R.id.guidelineHorizontalCenter1, segHeight);
adjustGuideline(R.id.guidelineHorizontalCenter2, segHeight);
adjustGuideline(R.id.guidelineHorizontalCenter3, segHeight);
adjustGuideline(R.id.guidelineHorizontalCenter4, segHeight);
adjustGuideline(R.id.guidelineHorizontalBottom, segHeight);
arrayOfCustomButtons = new CustomButton[]{buttonTopLeft, buttonTopRight, buttonBottomLeft,
buttonBottomRight, buttonMiddle, buttonMiddleTopLeft, getButtonMiddleTopRight};
rootView = findViewById(R.id.rootView);
for (final CustomButton cb : arrayOfCustomButtons) {
cb.setClickable(true);
cb.post(new Runnable() {
#Override
public void run() {
Log.i("MainActivity", "<<<< width of: " + cb.getTag() + " is: "
+ cb.getMeasuredWidth());
}
});
}
}
});
}
private void adjustGuideline(int guideLineId, int segHeight) {
Guideline gl = (Guideline) findViewById(guideLineId);
ConstraintLayout.LayoutParams lp = ((ConstraintLayout.LayoutParams) gl.getLayoutParams());
gl.setGuidelineBegin((int) (segHeight * lp.guidePercent / 0.08f));
gl.setGuidelinePercent(-1f);
}
}

How to properly extend TextInputLayout.java

public class PhoneInputLayout extends TextInputLayout {
public void setPhoneNumberEditText(TextInputEditText
phoneNumberEditText) {
this.phoneNumberEditText = phoneNumberEditText;
}
private TextInputEditText phoneNumberEditText;
public PhoneInputLayout(Context context ) {
super(context);
}
public PhoneInputLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override public void setError(#Nullable CharSequence error) {
phoneNumberEditText.setPaddingRelative(phoneNumberEditText.getPaddingStart() - getPadding(R.dimen.account_info_margin),
phoneNumberEditText.getPaddingTop(),
phoneNumberEditText.getPaddingEnd(),
phoneNumberEditText.getPaddingBottom());
super.setError(error);
}
private int getPadding(int paddingId) {
float scale = this.getContext().getResources().getDisplayMetrics().density;
return (int) (this.getContext().getResources().getDimension(paddingId) * scale + 0.5f);
}
}
All of the view classes defined in the Android framework extend View. Your custom view can also extend View directlyThis code doesn't show the cursor on the field. I want to understand how to properly extend the TextInputLayout class
I don't know why you want to create a custom view for TextInputLayout.
The following is working well with cursor on the field.
<android.support.design.widget.TextInputLayout
android:id="#+id/text_input_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:hint="Phone Number"
android:labelFor="#id/card_input_password"
android:paddingStart="12dp"
android:textColorHint="#color/text_tertiary_black"
app:errorEnabled="true"
app:hintTextAppearance="#style/TextInputLayoutFlybuysTheme">
<EditText
android:id="#id/card_input_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top|start"
android:inputType="phone" />
</android.support.design.widget.TextInputLayout>

TextView breaks my word by letters

My requirements: create "incoming bubble" with width by content and max width 90%.
I have this markup:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="1.0"
tools:background="#color/white_smoke">
<LinearLayout
android:id="#+id/flBubble"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:background="#drawable/bubble_in"
android:layout_weight="0.9">
<ImageView
android:id="#+id/ivSay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:contentDescription="#string/default_content_description"
android:padding="8dp"
android:src="#drawable/ic_play_circle_outline_black_24dp"
android:tint="#color/primary"/>
<TextView
android:id="#+id/tvValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="8dp"
android:textColor="#color/black"
android:textSize="16sp"
tools:text="I would like to go to an Italian restaurant"/>
</LinearLayout>
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="0.1"/>
</LinearLayout>
Sometimes I get the following result:
But I expect the following result (it's falsely encouraging screenshot from Android Studio preview):
How can I prevent breaking word restaraunt by letters?
UPDATE
Although I use minSdk=15 I tried to use breakStrategy and I haven't get expected result.
android:breakStrategy="simple":
android:breakStrategy="balanced":
I found a related question: Force next word to a new line if the word is too long for the textview, but I didn't undestand how can I get maximum available width for TextView with layout_width="wrap_content?
It would be great if I could override the TextView.setText and place line breaks there if needed.
OMG, there were in my string!
value.replaceAll("\\s", " ");
Thank you all!
Use MaxWidth property for textview or else you should provide width for textview
<com.custom.views.CustomTextView
android:id="#+id/txt_send_chat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:maxWidth="250dp"
android:textColor="#color/color_chat_sender"
android:textSize="16sp"
app:font_name="#string/font_roboto_regular" />
You can use webview to achieve this behavior.
In webview you can use css to adjust text.
Take a look at this answer
Update
You can calculate width of string and add \n to string where is string needs to split
Rect bounds = new Rect();
Paint textPaint = textView.getPaint();
textPaint.getTextBounds(text, 0, text.length(), bounds);
int height = bounds.height();
int width = bounds.width();
Results is in pixels, so just check width of your view or screen and split the string.
UPDAE2: Example Code
I just wrote an example with simple layout in activity onCreate you can implement it in adapter or whatever works for you.
TextView textView = (TextView) findViewById(R.id.txt); //textview with empty text
Rect bounds = new Rect();
Paint textPaint = textView.getPaint();
String text = "some long text here.....";// text data to work on
textPaint.getTextBounds(text, 0, text.length(), bounds);
int textWidth = bounds.width();// get text width in pixel
int marginPadding = 100;// we have some padding and margin from xml layouts
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int rootWidth = displayMetrics.widthPixels-marginPadding;// maximum width on screan
if (textWidth > rootWidth) { // check if need to split the string.
int lineMax = (text.length() * rootWidth) / textWidth; // maximum Characters for each line
String result = text.replaceAll("(.{" + String.valueOf(lineMax) + "})", "$1\n"); // regex to replace each group(lineMax) of Chars with group of char + new line
textView.setText(result);
} else
textView.setText(text);
UPDATE#3: Fixed code for Listview
onCreate
ArrayList<String> data = new ArrayList<>();
data.add("000");
data.add("aaaaaaaaaaa");
data.add("aaaaaaaaaaa bbbbbbbbbbbb");
data.add("aaaaaaaaaaa bbbbbbbbbbbb cccccccccccccccc");
data.add("aaaaaaaaaaa bbbbbbbbbbbb cccccccccccccccc ddddddddddddd");
data.add("aaaaaaaaaaa bbbbbbbbbbbb cccccccccccccccc ddddddddddddd eeeeeeeeeeeee");
data.add("aaaaaaaaaaa bbbbbbbbbbbb cccccccccccccccc ddddddddddddd eeeeeeeeeeeee ffffffffffffffffff");
data.add("aaaaaaaaaaa bbbbbbbbbbbb cccccccccccccccc ddddddddddddd eeeeeeeeeeeee ffffffffffffffffff gggggggggggggggg");
data.add("aaaaaaaaaaa bbbbbbbbbbbb cccccccccccccccc ddddddddddddd eeeeeeeeeeeee ffffffffffffffffff gggggggggggggggg hhhhhhhhhhhhhhhh");
ListView listView = (ListView) findViewById(R.id.listview);
MyAdapter adapter= new MyAdapter(data,this);
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();
MyAdapter.java
public class MyAdapter extends BaseAdapter {
private LayoutInflater inflater = null;
Context context;
ArrayList<String> data;
public MyAdapter(ArrayList<String> data, Context context) {
this.context = context;
this.data = data;
inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return data.size();
}
#Override
public Object getItem(int i) {
return data.get(i);
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public View getView(final int i, View convertView, ViewGroup viewGroup) {
final View view = inflater.inflate(R.layout.item, null);
final TextView tv_text = (TextView) view.findViewById(R.id.tvValue);
if (data.get(i) != null) {
tv_text.post(new Runnable() {
#Override
public void run() {
//TextView is Ready to be used.
fixText(data.get(i),tv_text);
}
});
}
return view;
}
private void fixText(String text, TextView textView) {
Rect bounds = new Rect();
Paint textPaint = textView.getPaint();
textPaint.getTextBounds(text, 0, text.length(), bounds);
int textWidth = bounds.width();// get text width in pixel
int marginPadding = 100;// we have some padding and margin from xml layouts
DisplayMetrics displayMetrics = new DisplayMetrics();
((MainActivity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int rootWidth = textView.getWidth();//displayMetrics.widthPixels - marginPadding;// maximum width on screan
if (textWidth > rootWidth) { // check if need to split the string.
//int lineMax = (text.length() * rootWidth) / textWidth; // maximum Characters for each line
//String result = text.replaceAll("(.{" + String.valueOf(lineMax-5) + "})", "$1\n"); // regex to replace each group(lineMax) of Chars with group of char + new line
String result = wrapText(rootWidth,text);
textView.setText(result);
} else
textView.setText(text);
}
private String wrapText(int textviewWidth,String mQuestion) {
String temp = "";
String sentence = "";
String[] array = mQuestion.split(" "); // split by space
for (String word : array) {
if ((temp.length() + word.length()) < textviewWidth) { // create a temp variable and check if length with new word exceeds textview width.
temp += " "+word;
} else {
sentence += temp+"\n"; // add new line character
temp = word;
}
}
return (sentence.replaceFirst(" ", "")+temp);
}
item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="1.0"
tools:background="#color/colorAccent">
<LinearLayout
android:id="#+id/flBubble"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:background="#color/colorPrimary"
android:layout_weight="0.9">
<ImageView
android:id="#+id/ivSay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:contentDescription="default_content_description"
android:padding="8dp"
android:src="#android:drawable/ic_media_play"
android:tint="#color/colorPrimaryDark" />
<TextView
android:id="#+id/tvValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="8dp"
android:textColor="#000000"
android:textSize="16sp"
tools:text="I would like to go to an Italian restaurant jkjk l;'"/>
</LinearLayout>
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="0.1"/>
</LinearLayout>
Try this
<TextView
android:id="#+id/tvValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="8dp"
android:textColor="#color/black"
android:textSize="16sp"
tools:text="I would like to go to an Italian restaurant"/>
</LinearLayout>
You can try with
Autosizing TextViews
The Support Library 26.0 provides full support to the autosizing TextView feature on devices running Android versions prior to Android 8.0 (API level 26). The library provides support to Android 4.0 (API level 14) and higher. The android.support.v4.widget package contains the TextViewCompat class to access features in a backward-compatible fashion
For Example:
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:autoSizeTextType="uniform" />
For more details Guidelines go HERE
Their is Library too HERE
Try this
private String getWidthFitString(String input) {
Paint paint = text.getPaint();
// you can define max width by your self
int maxWidth = getContentMaxWidth();
float width = paint.measureText(input);
if (width > maxWidth) {
List<String> words = Arrays.asList(input.split("\\s"));
int breakLinePosition = 0;
String toBreakLineText;
List<String> toBreakLineWords = new ArrayList<>();
while (breakLinePosition < words.size()) {
toBreakLineWords.add(words.get(breakLinePosition));
toBreakLineText = TextUtils.join(" ", toBreakLineWords);
float currentWidth = paint.measureText(toBreakLineText);
if (currentWidth > maxWidth) {
break;
}
breakLinePosition ++;
}
if (breakLinePosition > 1) {
toBreakLineWords.remove(toBreakLineWords.size() - 1);
toBreakLineText = TextUtils.join(" ", toBreakLineWords);
List<String> fromBreakLineWords = new ArrayList<>();
for (int i = breakLinePosition; i < words.size(); i++) {
fromBreakLineWords.add(words.get(i));
}
return toBreakLineText + "\n" + getWidthFitString(TextUtils.join(" ", fromBreakLineWords));
} else {
return input;
}
}
return input;
}
For those whose string has non-breaking space character may try the following:
value.replace("\u00A0", " ")
Hope this might help
Change your TextView to EditText and put this 2 lines. it should help you
android:inputType="textMultiLine"
android:enabled="false"
This will place you text properly and later on you can give a edit feature in your application if you need.

Getting 'java.lang.ClassCastException' while trying to set subtitle of a CollapsingToolbarLayout

I'm trying to set 'Sub-title' of a CollapsingToolbarLayout in my app using this example here.
Here's the code from onCreate() of Profile.java:
CollapsingToolbarLayout collapsingToolbarLayout;
Toolbar toolbar;
HeaderView toolbarHeaderView;
HeaderView floatHeaderView;
collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapse_toolbar);
// error on the line below
toolbarHeaderView = (HeaderView) findViewById(R.id.toolbar_header_view);
floatHeaderView = (HeaderView) findViewById(R.id.float_header_view);
toolbarHeaderView.bindTo("title", "subtitle");
floatHeaderView.bindTo("title", "subtitle");
Here's activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
android:id="#+id/coordinatorLayout"
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"
android:fitsSystemWindows="true"
tools:context="com.abc.zzz.Profile">
<android.support.design.widget.AppBarLayout
android:id="#+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="256dp"
android:theme="#style/AppTheme.AppBarOverlay"
android:fitsSystemWindows="true">
<android.support.design.widget.CollapsingToolbarLayout
android:id="#+id/collapse_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:contentScrim="#color/colorPrimary"
android:fitsSystemWindows="true"
app:popupTheme="#style/AppTheme.PopupOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="parallax">
<include
android:id="#+id/toolbar_header_view"
layout="#layout/header_view"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginRight="#dimen/header_view_end_margin_right"
android:layout_marginEnd="#dimen/header_view_end_margin_right"
android:visibility="gone"
/>
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<include
android:id="#+id/float_header_view"
layout="#layout/header_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="com.abc.zzz.ViewBehavior"/>
</android.support.design.widget.CoordinatorLayout>
Here's header_view.xml:
<?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">
<!-- Title -->
<TextView
android:id="#+id/header_view_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#android:color/white"
android:textSize="18sp"
/>
<!-- Subtitle -->
<TextView
android:id="#+id/header_view_sub_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#android:color/white"
android:textSize="16sp"
/>
</LinearLayout>
Here's HeaderView.java:
public class HeaderView extends LinearLayout {
TextView title;
TextView subTitle;
public HeaderView(Context context) {
super(context);
}
public HeaderView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public HeaderView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public HeaderView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
title = (TextView) findViewById(R.id.header_view_title);
subTitle = (TextView) findViewById(R.id.header_view_sub_title);
}
public void bindTo(String title) {
bindTo(title, "");
}
public void bindTo(String title, String subTitle) {
hideOrSetText(this.title, title);
hideOrSetText(this.subTitle, subTitle);
}
private void hideOrSetText(TextView tv, String text) {
if (text == null || text.equals(""))
tv.setVisibility(GONE);
else
tv.setText(text);
}
}
Here's ViewBehavior.java:
public class ViewBehavior extends CoordinatorLayout.Behavior<HeaderView> {
private Context mContext;
private int mStartMarginLeft;
private int mEndMargintLeft;
private int mMarginRight;
private int mStartMarginBottom;
private boolean isHide;
public ViewBehavior(Context context, AttributeSet attrs) {
mContext = context;
}
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, HeaderView child, View dependency) {
return dependency instanceof AppBarLayout;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, HeaderView child, View dependency) {
shouldInitProperties(child, dependency);
int maxScroll = ((AppBarLayout) dependency).getTotalScrollRange();
float percentage = Math.abs(dependency.getY()) / (float) maxScroll;
float childPosition = dependency.getHeight()
+ dependency.getY()
- child.getHeight()
- (getToolbarHeight() - child.getHeight()) * percentage / 2;
childPosition = childPosition - mStartMarginBottom * (1f - percentage);
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
lp.leftMargin = (int) (percentage * mEndMargintLeft) + mStartMarginLeft;
lp.rightMargin = mMarginRight;
child.setLayoutParams(lp);
child.setY(childPosition);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
if (isHide && percentage < 1) {
child.setVisibility(View.VISIBLE);
isHide = false;
} else if (!isHide && percentage == 1) {
child.setVisibility(View.GONE);
isHide = true;
}
}
return true;
}
private void shouldInitProperties(HeaderView child, View dependency) {
if (mStartMarginLeft == 0)
mStartMarginLeft = mContext.getResources().getDimensionPixelOffset(R.dimen.header_view_start_margin_left);
if (mEndMargintLeft == 0)
mEndMargintLeft = mContext.getResources().getDimensionPixelOffset(R.dimen.header_view_end_margin_left);
if (mStartMarginBottom == 0)
mStartMarginBottom = mContext.getResources().getDimensionPixelOffset(R.dimen.header_view_start_margin_bottom);
if (mMarginRight == 0)
mMarginRight = mContext.getResources().getDimensionPixelOffset(R.dimen.header_view_end_margin_right);
}
public int getToolbarHeight() {
int result = 0;
TypedValue tv = new TypedValue();
if (mContext.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
result = TypedValue.complexToDimensionPixelSize(tv.data, mContext.getResources().getDisplayMetrics());
}
return result;
}
}
The problem is that I'm getting this error: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.abc.zzz/com.abc.zzz.Profile}: java.lang.ClassCastException: android.widget.LinearLayout cannot be cast to com.abc.zzz.HeaderView on the line specified above.
Why am I getting this error and how to resolve it?
Please let me know.
You didn't show on your code but I bet that your header_view.xml have a LinearLayout as the root view.
So basically what happens is: the <include code "gets replaced" by the LinearLayout at the root of header_view.xml and then you call findViewById(R.id.toolbar_header_view) which returns that LinearLayout and then with the (HeaderView) you're telling the VM this is a HeaderView, but it's, it's a LinearLayout. So it crashes!
The best option without seeing piece of code you didn't show it is one of the following:
put <HeaderView> at the root of header_view.xml,
or if that is not possible because there's more stuff inside header_view.xml
change your code to find the include and then inside the include, to find the actual HeaderView.
Something like:
toolbarHeaderView = (HeaderView) findViewById(R.id.toolbar_header_view).findViewById(R.id.header_view_id);
floatHeaderView = (HeaderView) findViewById(R.id.float_header_view).findViewById(R.id.header_view_id);
note that it calls findViewById two times. One for the include and another for the HeaderView inside it

ImageView doesn't wrap content in RelativeLayout

I have a TableLayout with 6 childs/entrys. These childs are a custom RelativeLayout. In each RelativeLayout is a big TextView in the middle and an ImageView and small TextView at the bottom.
The ImageView should be as tall as the TextView next to it. That's why I set the attribute ALIGN_TOP and ALIGN_BOTTOM to the TextView (you can see it in code below). This works very well and both - ImageView and TextView - have the same height now. But the problem is, that the left and right side of the ImageView don't "wrap content" anymore (as you can see on the screenshot).
Is there a way to fit the left and right side to the image and remove the "padding"?
Here is my code:
view_display_component.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
<TextView
android:id="#+id/tvDisplayBig"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_weight="1"
android:gravity="center"
android:textColor="#color/white"
android:textSize="#dimen/font_size_extra_large" />
<ImageView
android:id="#+id/imageViewDisplayIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_below="#id/tvDisplayBig"
android:layout_gravity="bottom"
android:adjustViewBounds="true"
android:baselineAlignBottom="true"
android:scaleType="fitCenter"
android:src="#drawable/stopwatch_64"
android:visibility="visible" />
<TextView
android:id="#+id/tvDisplaySmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:gravity="bottom"
android:includeFontPadding="false"
android:textColor="#color/white"
android:textSize="#dimen/font_size_small" />
</merge>
class DisplayComponent which extends RelativLayout
public DisplayComponent(Context context) {
super(context);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.view_display_component, this, true);
tvDisplay = (TextView) getChildAt(0);
icon = (ImageView) getChildAt(1);
tvName = (TextView) getChildAt(2);
setupAlign();
}
private void setupAlign() {
if(index % 2 == 0) { // LEFT SIDE
// same as "RIGHT SIDE"
} else { // RIGHT SIDE
RelativeLayout.LayoutParams paramsIcon = (RelativeLayout.LayoutParams) icon.getLayoutParams();
paramsIcon.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
paramsIcon.addRule(RelativeLayout.ALIGN_TOP, tvName.getId());
paramsIcon.addRule(RelativeLayout.ALIGN_BOTTOM, tvName.getId());
icon.setLayoutParams(paramsIcon);
RelativeLayout.LayoutParams paramsTvName = (RelativeLayout.LayoutParams) tvName.getLayoutParams();
paramsTvName.addRule(RelativeLayout.RIGHT_OF, icon.getId());
tvName.setLayoutParams(paramsTvName);
tvName.setBackgroundColor(Color.BLUE); // only for testing
icon.setBackgroundColor(Color.YELLOW);
}
I found an (ugly) solution. Because my icon is square, I created a custom ImageView and overrode the onSizeChanged() method like this:
public class IconImageView extends ImageView {
public IconImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if(h != oldh && h > 0)
getLayoutParams().width = h; // same width as height
}
}
But this works only if the image is square. That's why I am still searching for a better solution. Maybe some layout solution with a better setting of alignment.
Best regards!

Categories

Resources