package com.forfan.bigbang.view; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.TargetApi; import android.content.ClipData; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.DashPathEffect; import android.graphics.Paint; import android.graphics.PathEffect; import android.graphics.Rect; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.support.v4.content.ContextCompat; import android.support.v4.view.MotionEventCompat; import android.support.v4.view.NestedScrollingChild; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.view.DragEvent; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.TextView; import com.forfan.bigbang.R; import com.forfan.bigbang.util.ConstantUtil; import com.forfan.bigbang.util.RegexUtil; import com.forfan.bigbang.util.ViewUtil; import com.shang.commonjar.contentProvider.SPHelper; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class BigBangLayout extends ViewGroup implements BigBangHeader.ActionListener, NestedScrollingChild { public static final String ENTER = "_Enter_"; public static final String ENTER_SYMBOL = "\n"; public static final String TAB = "_Tab_"; private static final int DEFAULT_TEXT_SIZE = 14;//sp private static final int DEFAULT_TEXT_COLOR_RES = R.color.bigbang_item_text; private static final int DEFAULT_TEXT_BG_RES = R.drawable.item_background; private static final int DEFAULT_SECTION_TEXT_BG_RES = R.drawable.item_background_section; private int mLineSpace; private int mItemSpace; private int mTextColorRes; private int mSectionTextBgRes; private int mTextSize; private int mTextPadding = (int) ViewUtil.dp2px(ConstantUtil.DEFAULT_ITEM_PADDING); private int mTextPaddingPort = (int) ViewUtil.dp2px(5); private int mTextBgRes; private Item mTargetItem; private List<Line> mLines=new ArrayList<>(); private List<Integer> mSectionIndex; private int mActionBarTopHeight; private int mActionBarBottomHeight; private BigBangHeader mHeader; private boolean showAnimation = false; private Paint dragPaint; private boolean dragMode = false; private boolean dragModeSelect = false; private Item dragItem; private ColorStateList mColorStateList; private boolean stickHeader = false; private int mOriginActionBarTopHeight; boolean autoAddBlanks = SPHelper.getBoolean(ConstantUtil.AUTO_ADD_BLANKS,false); private AnimatorListenerAdapter mActionBarAnimationListener = new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); if (findFirstSelectedLine() == null) { mHeader.setVisibility(View.GONE); } else { requestLayout(); } } }; private ActionListener mActionListener; private int mScaledTouchSlop; private float mDownX; private float mDownY; private boolean mDisallowedParentIntercept; private boolean showSymbol = false; private boolean showSection = false; private Rect mDragSelectRect; private Paint mDragSelectPaint; private float mDragSelectX,mDragSelectY; private Set<ItemState> mDragSelectSet; private boolean mDragSelection; private boolean mDragSelectionSetted; private boolean mNeedReDetectInMeasure=true; public BigBangLayout(Context context) { super(context); } public BigBangLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public BigBangLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(attrs); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public BigBangLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initView(attrs); } private void initView(AttributeSet attrs) { if (attrs != null) { TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.BigBangLayout); mItemSpace = typedArray.getDimensionPixelSize(R.styleable.BigBangLayout_itemSpace, getResources().getDimensionPixelSize(R.dimen.big_bang_default_item_space)); mLineSpace = typedArray.getDimensionPixelSize(R.styleable.BigBangLayout_lineSpace, getResources().getDimensionPixelSize(R.dimen.big_bang_default_line_space)); mTextColorRes = typedArray.getResourceId(R.styleable.BigBangLayout_textColor, DEFAULT_TEXT_COLOR_RES); mTextSize = (int) ViewUtil.px2sp(typedArray.getDimension(R.styleable.BigBangLayout_textSize, ViewUtil.sp2px(DEFAULT_TEXT_SIZE))); mTextBgRes = typedArray.getResourceId(R.styleable.BigBangLayout_textBackground, DEFAULT_TEXT_BG_RES); mSectionTextBgRes = typedArray.getResourceId(R.styleable.BigBangLayout_sectionTextBackground, DEFAULT_SECTION_TEXT_BG_RES); typedArray.recycle(); mActionBarBottomHeight = mLineSpace; mActionBarTopHeight = getResources().getDimensionPixelSize(R.dimen.big_bang_action_bar_height); mOriginActionBarTopHeight=mActionBarTopHeight; } // TODO 暂时放到这里 mHeader = new BigBangHeader(getContext()); mHeader.setVisibility(View.GONE); mHeader.setActionListener(this); dragPaint = new Paint(); // dragPaint.setAlpha(100); dragPaint.setAntiAlias(true); addView(mHeader, 0); setClipChildren(false); mScaledTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); setOnDragListener(new OnDragListener() { @Override public boolean onDrag(View v, DragEvent event) { int x = (int) event.getX(); int y = (int) event.getY(); int eventType = event.getAction(); // View view= (View) event.getLocalState(); // if (eventType==DragEvent.ACTION_DRAG_ENDED){ // return true; // } // String txt=event.getClipDescription().getLabel().toString(); try { if (dragItem == null || !(dragItem.getText().equals(event.getClipDescription().getLabel()))) { return false; } } catch (Throwable e) { return false; } Item item = findItemByPoint(x, y); Log.e("findItemIndexByPoint", "item=" + item + "," + (item != null ? item.index : -1)); if (item == null) { item = findItemIndexByPoint(x, y); if (item == null) { if (eventType == DragEvent.ACTION_DRAG_ENDED) { mNeedReDetectInMeasure=true; removeView(dragItem.view); addView(dragItem.view, dragItem.index); mTargetItem = null; } return true; } } if (mTargetItem != null && mTargetItem.view == item.view) { return true; } else { mNeedReDetectInMeasure=true; removeView(dragItem.view); addView(dragItem.view, item.index); dragItem.index = item.index; mTargetItem = item; } return true; } }); mSectionIndex = new ArrayList<>(); mDragSelectRect=new Rect(); mDragSelectPaint = new Paint(); mDragSelectPaint.setColor(getResources().getColor(R.color.colorPrimary)); mDragSelectPaint.setStyle(Paint.Style.STROKE); mDragSelectPaint.setStrokeWidth(ViewUtil.dp2px(2)); PathEffect effects = new DashPathEffect(new float[]{5,5,5,5},1); mDragSelectPaint.setPathEffect(effects); mDragSelectPaint.setAntiAlias(true); mDragSelectSet=new HashSet<>(); setWillNotDraw(false); } public void setStickHeader(boolean stickHeader) { this.stickHeader = stickHeader; mHeader.setStickHeader(stickHeader); if (stickHeader){ mActionBarTopHeight=0; }else { mActionBarTopHeight=mOriginActionBarTopHeight; } } public int getLineSpace() { return mLineSpace; } public void setLineSpace(int mLineSpace) { this.mLineSpace = mLineSpace; requestLayout(); } public int getItemSpace() { return mItemSpace; } public void setItemSpace(int mItemSpace) { this.mItemSpace = mItemSpace; requestLayout(); } public ColorStateList getTextColorStateList() { return mColorStateList; } public void setTextColorStateList(ColorStateList colorStateList) { // TODO: 2016/11/12 this.mColorStateList = colorStateList; } public int getTextSize() { return mTextSize; } public void setTextSize(int mTextSize) { this.mTextSize = mTextSize; mNeedReDetectInMeasure=true; if (mLines != null) { for (Line line : mLines) { List<Item> items = line.getItems(); for (Item item : items) { ((TextView) item.view).setTextSize(mTextSize); (item.view).setPadding(mTextPadding, mTextPaddingPort,mTextPadding, mTextPaddingPort); } } } } public void setTextPadding(int padding){ mTextPadding=padding; mNeedReDetectInMeasure=true; if (mLines != null) { for (Line line : mLines) { List<Item> items = line.getItems(); for (Item item : items) { (item.view).setPadding(mTextPadding, mTextPaddingPort,mTextPadding, mTextPaddingPort); } } } } public int getTextBgRes() { return mTextBgRes; } public void setTextBgRes(int mTextBgRes) { // TODO: 2016/11/12 this.mTextBgRes = mTextBgRes; } public void addTextItem(String text) { mNeedReDetectInMeasure=true; // if (TextUtils.isEmpty(text) || text.equals(" ")) { if (TextUtils.isEmpty(text)) { return; } if (text.contains(TAB)) { return; } TextView view = new TextView(getContext()); view.setText(text); view.setBackgroundResource(mTextBgRes); if (mColorStateList == null) { view.setTextColor(ContextCompat.getColorStateList(getContext(), mTextColorRes)); } else { view.setTextColor(mColorStateList); } view.setTextSize(mTextSize); view.setPadding(mTextPadding, mTextPaddingPort,mTextPadding, mTextPaddingPort); view.setGravity(Gravity.CENTER); addView(view); } public void reset() { mNeedReDetectInMeasure=true; showAnimation = false; for (int i = getChildCount() - 1; i >= 0; i--) { View child = getChildAt(i); if (mHeader == child) { mHeader.setVisibility(View.GONE); continue; } removeView(child); } } public boolean isShowSymbol() { return showSymbol; } public void setShowSymbol(boolean showSymbol) { this.showSymbol = showSymbol; mNeedReDetectInMeasure=true; requestLayout(); } public boolean isShowSection() { return showSection; } public void setShowSection(boolean showSection) { this.showSection = showSection; mNeedReDetectInMeasure=true; requestLayout(); } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (dragModeSelect){ canvas.drawRect(mDragSelectRect,mDragSelectPaint); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); if (mNeedReDetectInMeasure) { int contentWidthSize = widthSize - mHeader.getContentPadding(); int heightSize = 0; int childCount = getChildCount(); int measureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); mLines.clear(); mSectionIndex.clear(); Line currentLine = null; int currentLineWidth = contentWidthSize; boolean isEnter = true; for (int i = 0; i < childCount; i++) { View v = getChildAt(i); if (mHeader == v) { continue; } View child = (View) v; String content; content = ((TextView) child).getText().toString(); child.setVisibility(VISIBLE); if (!showSymbol) { if (RegexUtil.isSymbol(content)) { child.setVisibility(GONE); continue; } } if (content.contains(ENTER) || content.equals(ENTER_SYMBOL)) { child.setVisibility(GONE); mSectionIndex.add(i); isEnter = true; continue; } child.measure(measureSpec, measureSpec); if (currentLineWidth > 0) { currentLineWidth += mItemSpace; } currentLineWidth += child.getMeasuredWidth(); if (mLines.size() == 0 || currentLineWidth > contentWidthSize || (isEnter && showSection)) { heightSize += child.getMeasuredHeight(); currentLineWidth = child.getMeasuredWidth(); currentLine = new Line(mLines.size()); mLines.add(currentLine); } Item item = new Item(currentLine); item.view = child; item.index = i; item.width = child.getMeasuredWidth(); item.height = child.getMeasuredHeight(); if (currentLine.getItems() == null && (isEnter && showSection)) { int padding = child.getPaddingLeft(); child.setBackgroundResource(mSectionTextBgRes); child.setPadding(padding, mTextPaddingPort, padding, mTextPaddingPort); } else { int padding = child.getPaddingLeft(); child.setBackgroundResource(mTextBgRes); child.setPadding(padding, mTextPaddingPort, padding, mTextPaddingPort); } currentLine.addItem(item); isEnter=false; } mNeedReDetectInMeasure = false; } Line firstSelectedLine = findFirstSelectedLine(); Line lastSelectedLine = findLastSelectedLine(); if (firstSelectedLine != null && lastSelectedLine != null) { int selectedLineHeight = (lastSelectedLine.maxIndex - firstSelectedLine.maxIndex + 1) * (firstSelectedLine.getHeight() + mLineSpace); mHeader.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(selectedLineHeight, MeasureSpec.UNSPECIFIED)); } // int size = heightSize + getPaddingTop() + getPaddingBottom() + (mLines.size()) * mLineSpace + mActionBarTopHeight + mActionBarBottomHeight; int size = (mLines.size()>0?(mLines.size()*mLines.get(0).getHeight()):0) + getPaddingTop() + getPaddingBottom() + (mLines.size()) * mLineSpace + mActionBarTopHeight + mActionBarBottomHeight; super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY)); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int top; int left; int offsetTop; Line lastSelectedLine = findLastSelectedLine(); Line firstSelectedLine = findFirstSelectedLine(); for (int i = 0; i < mLines.size(); i++) { Line line = mLines.get(i); List<Item> items = line.getItems(); left = getPaddingLeft() + mHeader.getContentPadding(); if (firstSelectedLine != null && firstSelectedLine.maxIndex > line.maxIndex) { //如果在第一个被选中行以前,则需要上移mActionBarTopHeight的距离 offsetTop = -mActionBarTopHeight; } else if (lastSelectedLine != null && lastSelectedLine.maxIndex < line.maxIndex) { //如果在最后一个被选中行以后,则需要下移mActionBarBottomHeight的距离 offsetTop = mActionBarBottomHeight; } else { offsetTop = 0; } for (int j = 0; j < items.size(); j++) { Item item = items.get(j); top = getPaddingTop() + i * (item.height + mLineSpace) + offsetTop + mActionBarTopHeight; View child = item.view; int oldTop = child.getTop(); child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight()); if (showAnimation && oldTop != top) { int translationY = oldTop - top; child.setTranslationY(translationY); child.animate().translationYBy(-translationY).setDuration(200).start(); } left += child.getMeasuredWidth() + mItemSpace; } } if (!stickHeader) { if (firstSelectedLine != null && lastSelectedLine != null) { mHeader.setVisibility(View.VISIBLE); mHeader.setAlpha(1); int oldTop = mHeader.getTop(); int actionBarTop = firstSelectedLine.maxIndex * (firstSelectedLine.getHeight() + mLineSpace) + getPaddingTop(); mHeader.layout(getPaddingLeft(), actionBarTop, getPaddingLeft() + mHeader.getMeasuredWidth(), actionBarTop + mHeader.getMeasuredHeight()); if (oldTop != actionBarTop) { int translationY = oldTop - actionBarTop; mHeader.setTranslationY(translationY); mHeader.animate().translationYBy(-translationY).setDuration(200).start(); } } else { if (mHeader.getVisibility() == View.VISIBLE && !dragMode) { mHeader.animate().alpha(0).setDuration(200).setListener(mActionBarAnimationListener).start(); } } }else { mHeader.setVisibility(GONE); } } private Line findLastSelectedLine() { Line result = null; for (Line line : mLines) { if (line.hasSelected()) { result = line; } } return result; } private Line findFirstSelectedLine() { for (Line line : mLines) { if (line.hasSelected()) { return line; } } return null; } // @Override // public boolean onInterceptTouchEvent(MotionEvent ev) { // return true; // } @Override public boolean onTouchEvent(MotionEvent event) { Log.e("onTouchEvent", "onTouchEvent:" + event); int actionMasked = MotionEventCompat.getActionMasked(event); if (dragModeSelect){ int x = (int) event.getX(); int y = (int) event.getY(); switch (actionMasked) { case MotionEvent.ACTION_DOWN: showAnimation = true; mDownX = x; mDownY = y; mDisallowedParentIntercept = false; mDragSelectSet.clear(); case MotionEvent.ACTION_MOVE: if (!mDisallowedParentIntercept) { getParent().requestDisallowInterceptTouchEvent(true); mDisallowedParentIntercept = true; } mDragSelectX=x; mDragSelectY=y; if(mDragSelectX>getWidth()){ mDragSelectX=getWidth(); } if (mDragSelectX<0){ mDragSelectX=0; } if(mDragSelectY>getHeight()){ mDragSelectY=getHeight(); } if (mDragSelectY<0){ mDragSelectY=0; } mDragSelectRect.set((int)Math.min(mDownX,mDragSelectX), (int)Math.min(mDownY,mDragSelectY), (int)Math.max(mDownX,mDragSelectX), (int)Math.max(mDownY,mDragSelectY)); setSelectionByRect(mDragSelectRect); invalidate(); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: mDragSelectionSetted=false; dragModeSelect=false; mItemState=null; if (mActionListener!=null){ mActionListener.onDragSelectEnd(); } requestLayout(); invalidate(); if (mDisallowedParentIntercept) { getParent().requestDisallowInterceptTouchEvent(false); } break; } return true; } if (dragMode) { int x = (int) event.getX(); int y = (int) event.getY(); switch (actionMasked) { case MotionEvent.ACTION_DOWN: showAnimation = true; mDownX = x; mDownY = y; mDisallowedParentIntercept = false; Item item = findItemByPoint(x, y); if (item != null) { dragItem = new Item(item); ClipData clipData = ClipData.newPlainText(item.getText(), item.getText()); View.DragShadowBuilder myShadow = new DragShadowBuilder(dragItem.view); dragItem.view.startDrag(clipData, myShadow, null, 0); mNeedReDetectInMeasure=true; removeView(dragItem.view); } else { dragItem = null; } case MotionEvent.ACTION_MOVE: if (!mDisallowedParentIntercept && Math.abs(x - mDownX) > mScaledTouchSlop) { getParent().requestDisallowInterceptTouchEvent(true); mDisallowedParentIntercept = true; } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: // dragItem = null; if (mTargetItem == null && dragItem != null) { mNeedReDetectInMeasure=true; removeView(dragItem.view); addView(dragItem.view, dragItem.index); } mTargetItem = null; requestLayout(); invalidate(); if (mDisallowedParentIntercept) { getParent().requestDisallowInterceptTouchEvent(false); } break; } } else { int x = (int) event.getX(); int y = (int) event.getY(); switch (actionMasked) { case MotionEvent.ACTION_DOWN: longPressedHandler.removeCallbacks(mLongPressedRunnable); mLongPressedRunnable.setPosition(x,y); longPressedHandler.postDelayed(mLongPressedRunnable,500); showAnimation = true; mDownX = x; mDownY = y; mDisallowedParentIntercept = false; case MotionEvent.ACTION_MOVE: if (Math.abs(y - mDownY) > mScaledTouchSlop || Math.abs(x - mDownX) > mScaledTouchSlop) { longPressedHandler.removeCallbacks(mLongPressedRunnable); } if (!mDisallowedParentIntercept && Math.abs(x - mDownX) > mScaledTouchSlop) { getParent().requestDisallowInterceptTouchEvent(true); mDisallowedParentIntercept = true; } Item item = findItemByPoint(x, y); if (mTargetItem != item) { mTargetItem = item; if (item != null) { item.setSelected(!item.isSelected()); ItemState state = new ItemState(item,item.isSelected()); if (mItemState == null) { mItemState = state; } else { state.next = mItemState; mItemState = state; } } } break; case MotionEvent.ACTION_CANCEL: if (mItemState != null) { ItemState state = mItemState; while (state != null) { state.item.setSelected(!state.isSelected); state = state.next; } } case MotionEvent.ACTION_UP: longPressedHandler.removeCallbacks(mLongPressedRunnable); requestLayout(); invalidate(); mTargetItem = null; if (mDisallowedParentIntercept) { getParent().requestDisallowInterceptTouchEvent(false); } mItemState = null; String selected = makeSelectedText(); if (!TextUtils.isEmpty(selected)) { if (mActionListener != null) { mActionListener.onSelected(selected); } } break; } } return true; } private Handler longPressedHandler=new Handler(Looper.getMainLooper()); private LongPressedRunnable mLongPressedRunnable=new LongPressedRunnable(); private class LongPressedRunnable implements Runnable{ int x,y; public void setPosition(int x,int y){ this.x=x; this.y=y; } @Override public void run() { Item item=findItemByPoint(x,y); if (item!=null){ item.longPressed(); } } } ItemState mItemState; public void setBackgroundColorAlpha(int value) { value = (int) ((value / 100.0f) * 255); setBackgroundColor(Color.argb(value, 00, 00, 00)); } class ItemState { public ItemState(Item item,boolean isSelected){ this.item=item; this.isSelected=isSelected; } Item item; boolean isSelected; ItemState next; @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ItemState)) return false; ItemState itemState = (ItemState) o; return item != null ? item.equals(itemState.item) : itemState.item == null; } @Override public int hashCode() { return item != null ? item.hashCode() : 0; } } private Item findItemByPoint(int x, int y) { for (Line line : mLines) { List<Item> items = line.getItems(); for (Item item : items) { if (item.getRect().contains(x, y)) { return item; } } } return null; } private void setSelectionByRect(Rect rect){ for (Line line : mLines) { List<Item> items = line.getItems(); for (Item item : items) { if (item.getRect().intersect(rect)) { if (!mDragSelectionSetted){ mDragSelectionSetted=true; mDragSelection=!item.view.isSelected(); } ItemState state=new ItemState(item,item.view.isSelected()); if (!mDragSelectSet.contains(state)) { mDragSelectSet.add(state); } item.view.setSelected(mDragSelection); } } } for (ItemState item:mDragSelectSet){ if (!item.item.getRect().intersect(rect)) { item.item.view.setSelected(item.isSelected); } } } private Item findItemIndexByPoint(int x, int y) { int height = 0; int lineNum = 0; int length = mLines.size(); if (y > mLines.get(0).getHeight() / 2 + mActionBarTopHeight && y < getHeight() - mLines.get(0).getHeight() - mLineSpace) { // TODO: 2016/10/27 调整效果 return null; } // for (int i=0;i<length;i++) { // if (y-mLines.get(i).getHeight()/2<0 ){ // lingNum=i; // break; // } // if (height <= y-mLines.get(i).getHeight()/2+mLineSpace/2 && height+mLines.get(i).getHeight()+mLineSpace>= y-mLines.get(i).getHeight()/2+mLineSpace/2 ){ // lingNum=i; // break; // } // height+=mLines.get(i).getHeight()+mLineSpace; // lingNum=i; // } if (mLines.get(0).hasSelected() && y <= mActionBarTopHeight) { lineNum = 0; } else if (!mLines.get(0).hasSelected() && y <= mLines.get(0).getHeight() / 2 + mActionBarTopHeight) { lineNum = 0; } else if (y >= getHeight() - mLines.get(0).getHeight() - mLineSpace) { lineNum = mLines.size() - 1; } else { return null; } length = mLines.get(lineNum).getItems().size(); List<Item> items = mLines.get(lineNum).getItems(); height = 0; for (int i = 0; i < length; i++) { if (height <= x - items.get(i).view.getMeasuredWidth() / 2 + mItemSpace / 2 && height >= x - items.get(i).view.getMeasuredWidth() + mItemSpace / 2) { return items.get(i); } height += items.get(i).view.getMeasuredWidth() + mItemSpace; } if (height <= x - items.get(items.size() - 1).view.getMeasuredWidth() / 2) { return items.get(items.size() - 1); } return null; } private View findChildByPoint(int x, int y) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); Rect rect = new Rect(); child.getHitRect(rect); if (rect.contains(x, y)) { return child; } } return null; } private String makeSelectedText() { StringBuilder builder = new StringBuilder(); int length = mLines.size(); for (int i = 0; i < length; i++) { Line line = mLines.get(i); boolean containEnter = false; if (i < length - 1) { Line nextLine = mLines.get(i + 1); int thisLineLastIndex = line.getItems().get(line.getItems().size() - 1).index; int nextLineFirstIndex = nextLine.getItems().get(nextLine.getItems().size() - 1).index; for (int j = thisLineLastIndex; j < nextLineFirstIndex; j++) { if (mSectionIndex.contains(j)) { containEnter = true; } } } builder.append(line.getSelectedText()); if (containEnter&&showSection) { builder.append("\n"); } } return builder.toString().replaceAll("[\\n]+","\n").trim(); } @Override public void onSearch() { if (mActionListener != null) { String text = makeSelectedText(); mActionListener.onSearch(text); } } @Override public void onShare() { if (mActionListener != null) { String text = makeSelectedText(); mActionListener.onShare(text); } } @Override public void onCopy() { if (mActionListener != null) { String text = makeSelectedText(); mActionListener.onCopy(text); } } public void onDrag() { dragMode = !dragMode; if (mActionListener != null) { mActionListener.onDrag(); } } public void onDragSelect(boolean isDragSelcetion){ dragModeSelect=isDragSelcetion; } @Override public void onTrans() { if (mActionListener != null) { String text = makeSelectedText(); mActionListener.onTrans(text); } } public void onSelectOther() { for (Line line : mLines) { List<Item> items = line.getItems(); for (Item item : items) { // item.setSelected(!item.isSelected()); item.triggerSelected(); } } requestLayout(); } public void setActionListener(ActionListener actionListener) { mActionListener = actionListener; } class Line { int maxIndex; List<Item> items; public Line(int maxIndex) { this.maxIndex = maxIndex; } void addItem(Item item) { if (items == null) { items = new ArrayList<>(); } items.add(item); } List<Item> getItems() { return items; } boolean hasSelected() { int size = items.size(); for (int i=0;i<size;i++) { Item item = items.get(i); if (item.isSelected()) { return true; } } return false; } int getHeight() { List<Item> items = getItems(); if (items != null && items.size() > 0) { return items.get(0).view.getMeasuredHeight(); } return 0; } String getSelectedText() { StringBuilder builder = new StringBuilder(); List<Item> items = getItems(); if (items != null && items.size() > 0) { for (Item item : items) { if (item.isSelected()) { String txt = item.getText().toString(); builder.append(txt); if (autoAddBlanks) { if (txt.matches("[a-zA-Z0-9]*")) { builder.append(" "); } } } } } return builder.toString().replaceAll(" "," "); } } class Item { Line line; int index; int height; int width; View view; Rect rect=new Rect(); public Item(Line line) { this.line = line; } public Item(Item item) { if (item != null) { this.line = item.line; this.index = item.index; this.height = item.height; this.width = item.width; this.view = item.view; } } Rect getRect() { view.getHitRect(rect); return rect; } boolean isSelected() { return view.isSelected(); } void setSelected(boolean selected) { view.setSelected(selected); } void triggerSelected() { view.setSelected(!view.isSelected()); } CharSequence getText() { return ((TextView)view).getText().toString(); } void longPressed(){ if ( !(((TextView)view).getText().toString().matches("[a-zA-Z]*"))){ String text=getText().toString(); int size=text.length(); if (size<=1){ return; } mNeedReDetectInMeasure=true; ViewGroup viewgroup= (ViewGroup) view.getParent(); viewgroup.removeView(view); // int newPadding=(mTextPadding*2- mItemSpace*(size-1))/(size*2) ; //还是不考虑界面会变动的问题了,先保证好看 int newPadding=(mTextPadding) ; for (int i=0;i<size;i++){ TextView newTextView = new TextView(getContext()); newTextView.setText(text.substring(size-i-1,size-i)); newTextView.setBackgroundResource(mTextBgRes); if (mColorStateList == null) { newTextView.setTextColor(ContextCompat.getColorStateList(getContext(), mTextColorRes)); } else { newTextView.setTextColor(mColorStateList); } newTextView.setTextSize(mTextSize); newTextView.setPadding(newPadding, mTextPaddingPort,newPadding, mTextPaddingPort); newTextView.setGravity(Gravity.CENTER); viewgroup.addView(newTextView,index); } } } } /** * Action Listener */ public interface ActionListener { void onSelected(String text); void onSearch(String text); void onShare(String text); void onCopy(String text); void onTrans(String text); void onDrag(); void onDragSelectEnd(); } }