package com.glview.widget;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
import android.graphics.PorterDuff.Mode;
import android.graphics.Typeface;
import android.os.Build;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import com.glview.graphics.Bitmap;
import com.glview.graphics.Rect;
import com.glview.hwui.GLCanvas;
import com.glview.util.FastMath;
import com.glview.view.Gravity;
import com.glview.view.View;
public class TextView_Bak extends View {
private final static String TAG = "TextView";
public boolean isScaleView;
// protected MarqueeRender mTileRender;
private final TextPaint mTextPaint;
private float mTextSize;
private String mText;
private String mOldText;
private int mTextColor;
private Bitmap mTextBitmap;
private Canvas mTextCanvas;
private final Config mConfig;
private int mMaxWidth = Integer.MAX_VALUE;
private int mMinWidth = 0;
private final static int DEFAULT_TEXT_SIZE = 24;
private final static int DEFAULT_TEXT_COLOR = 0xffffffff;
TruncateAt mEllipsize = TruncateAt.END;
private int mGravity = Gravity.TOP | Gravity.START;
private float mShadowRadius, mShadowDx, mShadowDy;
public TextView_Bak(Context context) {
this(context, null);
}
public TextView_Bak(Context context, AttributeSet attrs) {
this(context, attrs, com.glview.R.attr.textViewStyle);
}
public TextView_Bak(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public TextView_Bak(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mConfig = Config.ARGB_4444;
isScaleView = true;
mTextSize = DEFAULT_TEXT_SIZE;
mTextColor = DEFAULT_TEXT_COLOR;
int ellipsize = -1;
float dx = 0, dy = 0, r = 0;
int shadowcolor = 0;
final Resources.Theme theme = context.getTheme();
final TypedArray a = theme.obtainStyledAttributes(attrs, com.glview.R.styleable.TextView, defStyleAttr, defStyleRes);
final int n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
if (attr == com.glview.R.styleable.TextView_autoLink) {
// mAutoLinkMask = a.getInt(attr, 0);
} else if (attr == com.glview.R.styleable.TextView_linksClickable) {
// mLinksClickable = a.getBoolean(attr, true);
} else if (attr == com.glview.R.styleable.TextView_maxLines) {
// setMaxLines(a.getInt(attr, -1));
} else if (attr == com.glview.R.styleable.TextView_maxHeight) {
// setMaxHeight(a.getDimensionPixelSize(attr, -1));
} else if (attr == com.glview.R.styleable.TextView_lines) {
// setLines(a.getInt(attr, -1));
} else if (attr == com.glview.R.styleable.TextView_height) {
// setHeight(a.getDimensionPixelSize(attr, -1));
} else if (attr == com.glview.R.styleable.TextView_minLines) {
// setMinLines(a.getInt(attr, -1));
} else if (attr == com.glview.R.styleable.TextView_minHeight) {
// setMinHeight(a.getDimensionPixelSize(attr, -1));
} else if (attr == com.glview.R.styleable.TextView_maxWidth) {
setMaxWidth(a.getDimensionPixelSize(attr, -1));
} else if (attr == com.glview.R.styleable.TextView_width) {
// setWidth(a.getDimensionPixelSize(attr, -1));
} else if (attr == com.glview.R.styleable.TextView_minWidth) {
setMinWidth(a.getDimensionPixelSize(attr, -1));
} else if (attr == com.glview.R.styleable.TextView_gravity) {
setGravity(a.getInt(attr, -1));
} else if (attr == com.glview.R.styleable.TextView_hint) {
// hint = a.getText(attr);
} else if (attr == com.glview.R.styleable.TextView_text) {
mText = (String) a.getText(attr);
} else if (attr == com.glview.R.styleable.TextView_scrollHorizontally) {
// if (a.getBoolean(attr, false)) {
// setHorizontallyScrolling(true);
// }
} else if (attr == com.glview.R.styleable.TextView_singleLine) {
// singleLine = a.getBoolean(attr, singleLine);
} else if (attr == com.glview.R.styleable.TextView_ellipsize) {
// ellipsize = a.getInt(attr, ellipsize);
ellipsize = a.getInt(attr, ellipsize);
} else if (attr == com.glview.R.styleable.TextView_marqueeRepeatLimit) {
// setMarqueeRepeatLimit(a.getInt(attr, mMarqueeRepeatLimit));
} else if (attr == com.glview.R.styleable.TextView_includeFontPadding) {
// if (!a.getBoolean(attr, true)) {
// setIncludeFontPadding(false);
// }
} else if (attr == com.glview.R.styleable.TextView_maxLength) {
// maxlength = a.getInt(attr, -1);
} else if (attr == com.glview.R.styleable.TextView_textScaleX) {
// setTextScaleX(a.getFloat(attr, 1.0f));
} else if (attr == com.glview.R.styleable.TextView_shadowColor) {
shadowcolor = a.getInt(attr, 0);
} else if (attr == com.glview.R.styleable.TextView_shadowDx) {
dx = a.getFloat(attr, 0);
} else if (attr == com.glview.R.styleable.TextView_shadowDy) {
dy = a.getFloat(attr, 0);
} else if (attr == com.glview.R.styleable.TextView_shadowRadius) {
r = a.getFloat(attr, 0);
} else if (attr == com.glview.R.styleable.TextView_enabled) {
setEnabled(a.getBoolean(attr, isEnabled()));
} else if (attr == com.glview.R.styleable.TextView_textColorHighlight) {
// textColorHighlight = a.getColor(attr, textColorHighlight);
} else if (attr == com.glview.R.styleable.TextView_textColor) {
// textColor = a.getColorStateList(attr);
mTextColor = a.getColor(attr, DEFAULT_TEXT_COLOR);
} else if (attr == com.glview.R.styleable.TextView_textColorHint) {
// textColorHint = a.getColorStateList(attr);
} else if (attr == com.glview.R.styleable.TextView_textColorLink) {
// textColorLink = a.getColorStateList(attr);
} else if (attr == com.glview.R.styleable.TextView_textSize) {
mTextSize = a.getDimensionPixelSize(attr, DEFAULT_TEXT_SIZE);
} else if (attr == com.glview.R.styleable.TextView_typeface) {
// typefaceIndex = a.getInt(attr, typefaceIndex);
} else if (attr == com.glview.R.styleable.TextView_textStyle) {
// styleIndex = a.getInt(attr, styleIndex);
} else if (attr == com.glview.R.styleable.TextView_fontFamily) {
// fontFamily = a.getString(attr);
}
}
a.recycle();
if (ellipsize < 0) {
ellipsize = 3; // END
}
switch (ellipsize) {
case 1:
setEllipsize(TruncateAt.START);
break;
case 2:
setEllipsize(TruncateAt.MIDDLE);
break;
case 3:
setEllipsize(TruncateAt.END);
break;
case 4:
setEllipsize(TruncateAt.MARQUEE);
break;
}
mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTextSize(mTextSize);
mTextPaint.setColor(mTextColor);
if(shadowcolor != 0){
setShadowLayer(r, dx, dy, shadowcolor);
}
}
/**
* Makes the TextView at least this many pixels wide
*
* @attr ref android.R.styleable#TextView_minWidth
*/
public void setMinWidth(int minpixels) {
mMinWidth = minpixels;
requestLayout();
invalidate();
}
/**
* @return the minimum width of the TextView, in pixels or -1 if the minimum width
* was set in ems instead (using {@link #setMinEms(int)} or {@link #setEms(int)}).
*
* @see #setMinWidth(int)
* @see #setWidth(int)
*
* @attr ref android.R.styleable#TextView_minWidth
*/
public int getMinWidth() {
return mMinWidth;
}
/**
* Makes the TextView at most this many pixels wide
*
* @attr ref android.R.styleable#TextView_maxWidth
*/
public void setMaxWidth(int maxpixels) {
mMaxWidth = maxpixels;
requestLayout();
invalidate();
}
/**
* @return the maximum width of the TextView, in pixels or -1 if the maximum width
* was set in ems instead (using {@link #setMaxEms(int)} or {@link #setEms(int)}).
*
* @see #setMaxWidth(int)
* @see #setWidth(int)
*
* @attr ref android.R.styleable#TextView_maxWidth
*/
public int getMaxWidth() {
return mMaxWidth;
}
//interface
@Override
protected void onFocusChanged(boolean gainFocus, int direction,
Rect previouslyFocusedRect) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
}
public enum TruncateAt {
START,
MIDDLE,
END,
MARQUEE,
/**
* @hide
*/
END_SMALL
}
public void setEllipsize(TruncateAt where) {
// TruncateAt is an enum. != comparison is ok between these singleton objects.
mEllipsize = where;
}
public void setText(String text){
mText = text;
requestLayout();
}
public void setText(CharSequence cs) {
setText(cs != null ? cs.toString() : null);
}
public void setText(int resId) {
setText(resId > 0 ? getContext().getResources().getText(resId) : null);
}
public void setTextColor(int textColor){
mTextPaint.setColor(textColor);
requestLayout();
}
public void setTextSize(float textSize){
mTextPaint.setTextSize(textSize);
requestLayout();
}
public float getTextSize(){
return mTextPaint.getTextSize();
}
public int getTextColor(){
return mTextPaint.getColor();
}
public CharSequence getText(){
return mText;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int width;
int height;
if (widthMode == MeasureSpec.EXACTLY) {
// Parent has told us how big to be. So be it.
width = widthSize;
} else {
width = mText != null ? (int)mTextPaint.measureText(mText) : 0;
width += getPaddingLeft() + getPaddingRight();
// Check against our minimum width
width = Math.max(width, getSuggestedMinimumWidth());
width = Math.min(width, mMaxWidth);
width = Math.max(width, mMinWidth);
if (widthMode == MeasureSpec.AT_MOST) {
width = Math.min(widthSize, width);
}
}
if (heightMode == MeasureSpec.EXACTLY) {
// Parent has told us how big to be. So be it.
height = heightSize;
} else {
int desired = getLineHeight();
height = desired;
height += getPaddingTop() + getPaddingBottom();
if (heightMode == MeasureSpec.AT_MOST) {
height = Math.min(height, heightSize);
}
}
if(mText != null){
boolean changed = false;
if(mOldText != null){
changed = (!mText.equals(mOldText))
|| mTextColor != mTextPaint.getColor()
|| mTextSize != mTextPaint.getTextSize();
if(changed){
}
} else {
changed = true;
}
if(changed){
renderText(width - getPaddingLeft() - getPaddingRight(), height - getPaddingTop() - getPaddingBottom());
}
} else {
}
mOldText = mText;
mTextColor = mTextPaint.getColor();
mTextSize = mTextPaint.getTextSize();
setMeasuredDimension(width, height);
}
public int getLineHeight(){
return FastMath.round(mTextPaint.getFontMetricsInt(null));
}
/**
* Sets the typeface and style in which the text should be displayed,
* and turns on the fake bold and italic bits in the Paint if the
* Typeface that you provided does not have all the bits in the
* style that you specified.
*
*/
public void setTypeface(Typeface tf, int style) {
if (style > 0) {
if (tf == null) {
tf = Typeface.defaultFromStyle(style);
} else {
tf = Typeface.create(tf, style);
}
setTypeface(tf);
// now compute what (if any) algorithmic styling is needed
int typefaceStyle = tf != null ? tf.getStyle() : 0;
int need = style & ~typefaceStyle;
mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
} else {
mTextPaint.setFakeBoldText(false);
mTextPaint.setTextSkewX(0);
setTypeface(tf);
}
}
/**
* Sets the typeface and style in which the text should be displayed.
* Note that not all Typeface families actually have bold and italic
* variants, so you may need to use
* {@link #setTypeface(Typeface, int)} to get the appearance
* that you actually want.
*
* @see #getTypeface()
*
*/
public void setTypeface(Typeface tf) {
if (mTextPaint.getTypeface() != tf) {
mTextPaint.setTypeface(tf);
}
}
/**
* @return the current typeface and style in which the text is being
* displayed.
*
* @see #setTypeface(Typeface)
*
*/
public Typeface getTypeface(){
return mTextPaint.getTypeface();
}
/**
* Gives the text a shadow of the specified radius and color, the specified
* distance from its normal position.
*
* @attr ref android.R.styleable#TextView_shadowColor
* @attr ref android.R.styleable#TextView_shadowDx
* @attr ref android.R.styleable#TextView_shadowDy
* @attr ref android.R.styleable#TextView_shadowRadius
*/
public void setShadowLayer(float radius, float dx, float dy, int color) {
mTextPaint.setShadowLayer(radius, dx, dy, color);
mShadowRadius = radius;
mShadowDx = dx;
mShadowDy = dy;
// Will change text clip region
}
int getVerticalOffset() {
int voffset = 0;
final int gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
if (gravity != Gravity.TOP) {
int boxht = getHeight() - getPaddingTop() - getPaddingBottom();
int textht = getLineHeight();
if (textht < boxht) {
if (gravity == Gravity.BOTTOM)
voffset = boxht - textht;
else // (gravity == Gravity.CENTER_VERTICAL)
voffset = (boxht - textht) >> 1;
}
}
return voffset;
}
int getHorizontalOffset() {
int voffset = 0;
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
final int gravity = absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
if (gravity != Gravity.LEFT) {
int boxwh = getWidth() - getPaddingLeft() - getPaddingRight();
int textwh = getContentWidth();
if (textwh < boxwh) {
if (gravity == Gravity.RIGHT)
voffset = boxwh - textwh;
else // (gravity == Gravity.CENTER_VERTICAL)
voffset = (boxwh - textwh) >> 1;
}
}
return voffset;
}
int getContentWidth() {
if (mTextBitmap != null) {
return mTextBitmap.getWidth();
}
return getWidth();
}
@Override
protected void onDraw(GLCanvas canvas) {
if(mTextBitmap != null && !TextUtils.isEmpty(mText)) {
final int compoundPaddingLeft = getPaddingLeft();
final int compoundPaddingTop = getPaddingTop();
final int compoundPaddingRight = getPaddingRight();
final int compoundPaddingBottom = getPaddingBottom();
canvas.save();
canvas.translate(getHorizontalOffset() + compoundPaddingLeft, getVerticalOffset() + compoundPaddingTop);
canvas.drawBitmap(mTextBitmap, 0, 0, null);
canvas.restore();
}
}
/**
* Sets the horizontal alignment of the text and the
* vertical gravity that will be used when there is extra space
* in the TextView beyond what is required for the text itself.
*
* @see android.view.Gravity
*/
public void setGravity(int gravity) {
if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
gravity |= Gravity.START;
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
gravity |= Gravity.TOP;
}
boolean newLayout = false;
if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) !=
(mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK)) {
newLayout = true;
}
if (gravity != mGravity) {
mGravity = gravity;
}
}
private void renderText(int width, int height) {
if (TextUtils.isEmpty(mText)) return;
FontMetricsInt metrics = mTextPaint.getFontMetricsInt();
// The texture size needs to be at least 1x1.
if (width <= 0) width = 1;
if (height <= 0) height = 1;
width = Math.min(width, (int) mTextPaint.measureText(mText));
height = Math.min(height, getLineHeight());
if (mTextBitmap == null) {
mTextBitmap = Bitmap.createBitmap(width, height, mConfig);
} /*else if ((width != mTextBitmap.getWidth() || height != mTextBitmap.getHeight()) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
try {
mTextBitmap.reconfigure(width, height, mConfig);
} catch (IllegalArgumentException e) {
mTextBitmap = Bitmap.createBitmap(width, height, mConfig);
}
} */else if (width > mTextBitmap.getWidth() || height > mTextBitmap.getHeight()) {
mTextBitmap = Bitmap.createBitmap(width, height, mConfig);
}
mTextCanvas = new Canvas(mTextBitmap.getBitmap());
// clear
mTextCanvas.drawColor(Color.TRANSPARENT, Mode.SRC);
mTextCanvas.translate(0, - metrics.ascent);
mTextCanvas.drawText(measureText(mTextPaint, mText, width, mEllipsize), 0, 0, mTextPaint);
invalidate();
}
private static String measureText(Paint paint, String text, int width, TruncateAt ellipsize) {
if (width <= 0) {
return text;
}
int w = width;
if (paint.measureText(text, 0, text.length()) <= w)
return text;
int i;
String str = null;
if (ellipsize == TruncateAt.START) {
for (i = 0; i < text.length(); i ++) {
str = "..." + text.substring(i, text.length());
float w1 = paint.measureText(str);
if (w1 < w) {
break;
}
}
} else {
for (i = text.length(); i > 0; i --) {
str = text.substring(0, i) + "...";
float w1 = paint.measureText(str);
if (w1 < w) {
break;
}
}
}
return str;
}
}