Androidで垂直ラベルを表示する2つの方法が必要です。
両方のケース(1つのケース)のカスタムウィジェットを開発する必要がありますか?TextViewをそのようにレンダリングすることはできますか?完全にカスタム化する必要がある場合、そのようなことを行う良い方法は何でしょうか?
以下は、TextViewを拡張した、エレガントでシンプルな垂直テキストの実装です。これは、TextViewが拡張されたTextViewであるため、TextViewのすべての標準スタイルを使用できることを意味します。
public class VerticalTextView extends TextView{
final boolean topDown;
public VerticalTextView(Context context, AttributeSet attrs){
super(context, attrs);
final int gravity = getGravity();
if(Gravity.isVertical(gravity) && (gravity&Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) {
setGravity((gravity&Gravity.HORIZONTAL_GRAVITY_MASK) | Gravity.TOP);
topDown = false;
}else
topDown = true;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
@Override
protected boolean setFrame(int l, int t, int r, int b){
return super.setFrame(l, t, l+(b-t), t+(r-l));
}
@Override
public void draw(Canvas canvas){
if(topDown){
canvas.translate(getHeight(), 0);
canvas.rotate(90);
}else {
canvas.translate(0, getWidth());
canvas.rotate(-90);
}
canvas.clipRect(0, 0, getWidth(), getHeight(), Android.graphics.Region.Op.REPLACE);
super.draw(canvas);
}
}
デフォルトでは、回転したテキストは上から下に向かって表示されます。 Android:gravity = "bottom"を設定すると、下から上に描画されます。
技術的には、基になるTextViewを欺いて、通常の回転(いくつかの場所で幅/高さを交換する)であると考え、回転させながら描画します。 xmlレイアウトで使用する場合にも正常に機能します。
編集:別のバージョンを投稿すると、上記のアニメーションに問題があります。この新しいバージョンはより良く機能しますが、Marqueeや同様の専門分野など、TextViewの一部の機能が失われます。
public class VerticalTextView extends TextView{
final boolean topDown;
public VerticalTextView(Context context, AttributeSet attrs){
super(context, attrs);
final int gravity = getGravity();
if(Gravity.isVertical(gravity) && (gravity&Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) {
setGravity((gravity&Gravity.HORIZONTAL_GRAVITY_MASK) | Gravity.TOP);
topDown = false;
}else
topDown = true;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(heightMeasureSpec, widthMeasureSpec);
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
}
@Override
protected void onDraw(Canvas canvas){
TextPaint textPaint = getPaint();
textPaint.setColor(getCurrentTextColor());
textPaint.drawableState = getDrawableState();
canvas.save();
if(topDown){
canvas.translate(getWidth(), 0);
canvas.rotate(90);
}else {
canvas.translate(0, getHeight());
canvas.rotate(-90);
}
canvas.translate(getCompoundPaddingLeft(), getExtendedPaddingTop());
getLayout().draw(canvas);
canvas.restore();
}
}
ChartDroid プロジェクトにこれを実装しました。 VerticalLabelView.Java
を作成:
public class VerticalLabelView extends View {
private TextPaint mTextPaint;
private String mText;
private int mAscent;
private Rect text_bounds = new Rect();
final static int DEFAULT_TEXT_SIZE = 15;
public VerticalLabelView(Context context) {
super(context);
initLabelView();
}
public VerticalLabelView(Context context, AttributeSet attrs) {
super(context, attrs);
initLabelView();
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VerticalLabelView);
CharSequence s = a.getString(R.styleable.VerticalLabelView_text);
if (s != null) setText(s.toString());
setTextColor(a.getColor(R.styleable.VerticalLabelView_textColor, 0xFF000000));
int textSize = a.getDimensionPixelOffset(R.styleable.VerticalLabelView_textSize, 0);
if (textSize > 0) setTextSize(textSize);
a.recycle();
}
private final void initLabelView() {
mTextPaint = new TextPaint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(DEFAULT_TEXT_SIZE);
mTextPaint.setColor(0xFF000000);
mTextPaint.setTextAlign(Align.CENTER);
setPadding(3, 3, 3, 3);
}
public void setText(String text) {
mText = text;
requestLayout();
invalidate();
}
public void setTextSize(int size) {
mTextPaint.setTextSize(size);
requestLayout();
invalidate();
}
public void setTextColor(int color) {
mTextPaint.setColor(color);
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mTextPaint.getTextBounds(mText, 0, mText.length(), text_bounds);
setMeasuredDimension(
measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec));
}
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the text
result = text_bounds.height() + getPaddingLeft() + getPaddingRight();
if (specMode == MeasureSpec.AT_MOST) {
// Respect AT_MOST value if that was what is called for by measureSpec
result = Math.min(result, specSize);
}
}
return result;
}
private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
mAscent = (int) mTextPaint.ascent();
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the text
result = text_bounds.width() + getPaddingTop() + getPaddingBottom();
if (specMode == MeasureSpec.AT_MOST) {
// Respect AT_MOST value if that was what is called for by measureSpec
result = Math.min(result, specSize);
}
}
return result;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float text_horizontally_centered_Origin_x = getPaddingLeft() + text_bounds.width()/2f;
float text_horizontally_centered_Origin_y = getPaddingTop() - mAscent;
canvas.translate(text_horizontally_centered_Origin_y, text_horizontally_centered_Origin_x);
canvas.rotate(-90);
canvas.drawText(mText, 0, 0, mTextPaint);
}
}
そして、attrs.xml
:
<resources>
<declare-styleable name="VerticalLabelView">
<attr name="text" format="string" />
<attr name="textColor" format="color" />
<attr name="textSize" format="dimension" />
</declare-styleable>
</resources>
これらを達成する1つの方法は次のとおりです。
承認済みの回答でVerticalTextViewクラスの両方を試してみましたが、うまく機能しました。
しかし、私が何を試したとしても、それらのVerticalTextViewを含むレイアウト(RecyclerView用に膨らんだアイテムの一部であるRelativeLayout)の中央に配置することができませんでした。
FWIW、周りを見回した後、GitHubでyoog568のVerticalTextViewクラスを見つけました。
https://github.com/yoog568/VerticalTextView/blob/master/src/com/yoog/widget/VerticalTextView.Java
希望どおりに配置できました。また、プロジェクトに次の属性定義を含める必要があります。
https://github.com/yoog568/VerticalTextView/blob/master/res/values/attr.xml
check = (TextView)findViewById(R.id.check);
check.setRotation(-90);
これでうまくいきました。垂直に下がっている文字については-私は知らない。
注意が必要ないくつかの小さな事柄があります。
回転またはパスの方法を選択するときの文字セットに依存します。たとえば、ターゲット文字セットが英語のようなもので、予想される効果が次のように見える場合、
a
b
c
d
この効果を得るには、各文字を1つずつ描画します。回転やパスは必要ありません。
この効果を得るには、回転またはパスが必要になる場合があります。
トリッキーな部分は、モンゴルのような文字セットをレンダリングしようとするときです。 Typefaceのグリフは90度回転する必要があるため、drawTextOnPath()を使用することをお勧めします。
Pointer Null の答えに従って、onDraw
メソッドをこのように変更することにより、テキストを水平方向に中央揃えすることができました。
@Override
protected void onDraw(Canvas canvas){
TextPaint textPaint = getPaint();
textPaint.setColor(getCurrentTextColor());
textPaint.drawableState = getDrawableState();
canvas.save();
if(topDown){
canvas.translate(getWidth()/2, 0);
canvas.rotate(90);
}else{
TextView temp = new TextView(getContext());
temp.setText(this.getText().toString());
temp.setTypeface(this.getTypeface());
temp.measure(0, 0);
canvas.rotate(-90);
int max = -1 * ((getWidth() - temp.getMeasuredHeight())/2);
canvas.translate(canvas.getClipBounds().left, canvas.getClipBounds().top - max);
}
canvas.translate(getCompoundPaddingLeft(), getExtendedPaddingTop());
getLayout().draw(canvas);
canvas.restore();
}
複数行のテキストを中央に配置するには、TextView measuredWidthの一部を追加する必要がある場合があります。
TextViewまたは他のView xml回転値に追加するだけです。これが最も簡単な方法であり、私にとっては正しく動作します。
<LinearLayout
Android:rotation="-90"
Android:layout_below="@id/image_view_qr_code"
Android:layout_above="@+id/text_view_savva_club"
Android:layout_marginTop="20dp"
Android:gravity="bottom"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:orientation="vertical">
<TextView
Android:textColor="@color/colorPrimary"
Android:layout_marginStart="40dp"
Android:textSize="20sp"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:text="Дмитриевский Дмитрий Дмитриевич"
Android:maxLines="2"
Android:id="@+id/vertical_text_view_name"/>
<TextView
Android:textColor="#B32B2A29"
Android:layout_marginStart="40dp"
Android:layout_marginTop="15dp"
Android:textSize="16sp"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:id="@+id/vertical_text_view_phone"
Android:text="+38 (000) 000-00-00"/>
</LinearLayout>
@kostmoのアプローチが気に入りました。私は問題を抱えていたので、それを少し変更しました-paramsを_WRAP_CONTENT
_に設定すると、垂直に回転したラベルが切れます。したがって、テキストは完全に表示されませんでした。
これは私がそれを解決した方法です:
_import Android.annotation.TargetApi;
import Android.content.Context;
import Android.graphics.Canvas;
import Android.graphics.Paint;
import Android.graphics.Rect;
import Android.graphics.Typeface;
import Android.os.Build;
import Android.text.TextPaint;
import Android.util.AttributeSet;
import Android.util.Log;
import Android.util.TypedValue;
import Android.view.View;
import Android.view.ViewGroup;
import Android.widget.TextView;
public class VerticalLabelView extends View
{
private final String LOG_TAG = "VerticalLabelView";
private final int DEFAULT_TEXT_SIZE = 30;
private int _ascent = 0;
private int _leftPadding = 0;
private int _topPadding = 0;
private int _rightPadding = 0;
private int _bottomPadding = 0;
private int _textSize = 0;
private int _measuredWidth;
private int _measuredHeight;
private Rect _textBounds;
private TextPaint _textPaint;
private String _text = "";
private TextView _tempView;
private Typeface _typeface = null;
private boolean _topToDown = false;
public VerticalLabelView(Context context)
{
super(context);
initLabelView();
}
public VerticalLabelView(Context context, AttributeSet attrs)
{
super(context, attrs);
initLabelView();
}
public VerticalLabelView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
initLabelView();
}
@TargetApi(Build.VERSION_CODES.Lollipop)
public VerticalLabelView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
{
super(context, attrs, defStyleAttr, defStyleRes);
initLabelView();
}
private final void initLabelView()
{
this._textBounds = new Rect();
this._textPaint = new TextPaint();
this._textPaint.setAntiAlias(true);
this._textPaint.setTextAlign(Paint.Align.CENTER);
this._textPaint.setTextSize(DEFAULT_TEXT_SIZE);
this._textSize = DEFAULT_TEXT_SIZE;
}
public void setText(String text)
{
this._text = text;
requestLayout();
invalidate();
}
public void topToDown(boolean topToDown)
{
this._topToDown = topToDown;
}
public void setPadding(int padding)
{
setPadding(padding, padding, padding, padding);
}
public void setPadding(int left, int top, int right, int bottom)
{
this._leftPadding = left;
this._topPadding = top;
this._rightPadding = right;
this._bottomPadding = bottom;
requestLayout();
invalidate();
}
public void setTextSize(int size)
{
this._textSize = size;
this._textPaint.setTextSize(size);
requestLayout();
invalidate();
}
public void setTextColor(int color)
{
this._textPaint.setColor(color);
invalidate();
}
public void setTypeFace(Typeface typeface)
{
this._typeface = typeface;
this._textPaint.setTypeface(typeface);
requestLayout();
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
try
{
this._textPaint.getTextBounds(this._text, 0, this._text.length(), this._textBounds);
this._tempView = new TextView(getContext());
this._tempView.setPadding(this._leftPadding, this._topPadding, this._rightPadding, this._bottomPadding);
this._tempView.setText(this._text);
this._tempView.setTextSize(TypedValue.COMPLEX_UNIT_PX, this._textSize);
this._tempView.setTypeface(this._typeface);
this._tempView.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
this._measuredWidth = this._tempView.getMeasuredHeight();
this._measuredHeight = this._tempView.getMeasuredWidth();
this._ascent = this._textBounds.height() / 2 + this._measuredWidth / 2;
setMeasuredDimension(this._measuredWidth, this._measuredHeight);
}
catch (Exception e)
{
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
Log.e(LOG_TAG, Log.getStackTraceString(e));
}
}
@Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
if (!this._text.isEmpty())
{
float textHorizontallyCenteredOriginX = this._measuredHeight / 2f;
float textHorizontallyCenteredOriginY = this._ascent;
canvas.translate(textHorizontallyCenteredOriginY, textHorizontallyCenteredOriginX);
float rotateDegree = -90;
float y = 0;
if (this._topToDown)
{
rotateDegree = 90;
y = this._measuredWidth / 2;
}
canvas.rotate(rotateDegree);
canvas.drawText(this._text, 0, y, this._textPaint);
}
}
}
_
上から下にテキストを表示する場合は、topToDown(true)
メソッドを使用します。