I am trying to set the layout exactly like this with two TextView and One ImageView . Any one can help me to set a view like this using android XML
-
Possible duplicate of [How to layout text to flow around an image](http://stackoverflow.com/questions/2248759/how-to-layout-text-to-flow-around-an-image) and [How to align TextView around an ImageView?](http://stackoverflow.com/questions/11494158/how-to-align-textview-around-an-imageview) – Shobhit Puri Apr 29 '16 at 04:00
1 Answers
you can use DynamicLayout with SpannableStringBuilder instead of TextView to accomplish it.
first, you will need to know the bound of the ImageView,it would be much easier if you put DynamicLayout and ImageView in a customize ViewGroup cause this will put them in same coordinate space.
second,get the first line in DynamicLayout with method getLineForVertical based on Bound rect's top value.and get the closest offset to the Bound rect's left value with DynamicLayout method getOffsetForHorizontal, then you insert a space character after the offset, and create a ImageSpan with a empty drawable with a width of Bound rect's width and put the ImageSpan in the SpannableStringBuilder,and make span start with offset and end with offset + 1.
third,line++ and repeat second step until getLineTop(line) value is bigger than Bound rect's bottom.
I wrote a demo for you, you can move the Rectangle inside it to see how DynamicLayout will relayout for the Rectangle,but make sure you don't move the rectangle outside the DynamicLayout, it will crash. I didn't solve this cause it's just a demo.
public class DynamicRichText extends View {
private DynamicLayout mLayout;
private Point mCirclePosition;
private int mCircleRadius;
private Rect mRect;
private TextPaint mTextPaint;
private int latestFirstLine;
private int latestLastLine;
private int latestOffsetStart;
private int latestOffsetEnd;
private SpannableStringBuilder mText;
public DynamicRichText(Context context){
this(context, null);
}
public DynamicRichText(Context context, AttributeSet attrs){
this(context, attrs, 0);
}
public DynamicRichText(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
mText = new SpannableStringBuilder("abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyz");
mRect = new Rect();
mCirclePosition = new Point();
mCircleRadius = 120;
mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTextSize(80);
mTextPaint.setColor(0xFF0099CC);
}
@Override
public boolean onTouchEvent(MotionEvent event){
if(event.getActionMasked() == MotionEvent.ACTION_MOVE){
mCirclePosition.x = (int) event.getX();
mCirclePosition.y = (int) event.getY();
mRect.left = mCirclePosition.x - mCircleRadius;
mRect.right = mCirclePosition.x + mCircleRadius;
mRect.top = mCirclePosition.y - mCircleRadius;
mRect.bottom = mCirclePosition.y + mCircleRadius;
resolveLayout();
invalidate();
}
return true;
}
private void resolveLayout(){
Object[] spans = mText.getSpans(latestOffsetStart, latestOffsetEnd, Object.class);
for(int i = 0; i < spans.length; i++){
int start = mText.getSpanStart(spans[i]);
if(spans[i] instanceof ImageSpan) mText.delete(start, start + 1);
mText.removeSpan(spans[i]);
}
int line = mLayout.getLineForVertical(mRect.top);
int offsetStart = mLayout.getOffsetForHorizontal(line, mRect.left) - 1;
latestOffsetStart = offsetStart;
while(mLayout.getLineTop(line) < mRect.bottom){
offsetStart = mLayout.getOffsetForHorizontal(line, mRect.left) - 1;
int lineHeight = mLayout.getLineBottom(line) - mLayout.getLineTop(line);
mText.insert(offsetStart, " ");
ColorDrawable emptyDrawable = new ColorDrawable(0x0);
emptyDrawable.setBounds(0, 0, mRect.width() + (int) mTextPaint.getTextSize(), lineHeight);
ImageSpan emptySpan = new ImageSpan(emptyDrawable);
mText.setSpan(emptySpan, offsetStart, offsetStart + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
line++;
}
latestOffsetEnd = offsetStart + 1;
}
@Override
public void onSizeChanged(int w, int h, int oldw, int oldh){
mCirclePosition = new Point(w / 2, h / 2);
mLayout = new DynamicLayout(mText, mText, mTextPaint, w, Layout.Alignment.ALIGN_NORMAL, 1, 0, false);
}
@Override
public void onDraw(Canvas canvas){
mTextPaint.setColor(0xFF000000);
canvas.drawRect(mRect, mTextPaint);
mTextPaint.setColor(0xFF0099CC);
mLayout.draw(canvas);
}
}
if you change it to extends ViewGroup or other Layout widget. and create Rect based on ImageView's bound.you can use this widget in xml

- 797
- 7
- 16
-
Thank you for your effort. I need to use this layout in a recyclerview and the image is populating from some URL . Will it work using this demo? – anoopg87 May 04 '16 at 12:07
-
it will, you should change mRect to fit the ImageView's bound everytime the ImageView's bound changed. then call resolveLayout() to re-generate the spans,and spans will create a empty space for the ImageView – 7heaven May 05 '16 at 02:42