안 드 로 이 드 리더 장 선택 문자 기능 구현 코드

앞에서 말 했 듯 이 가끔 우 리 는 문자 선택 기능 을 실현 해 야 한다.예 를 들 어 리더 가 보통 이런 기능 을 가지 고 있 고 가끔 은 특정한 사용자 정의 컨트롤 에 이런 수요 가 있 을 수 있다.어떻게 실현 합 니까?마침 요즘 은 여유 가 있어 서 자신 이 쓴 경량급 txt 파일 리 더 를 보완 하고 싶 습 니 다.그래서 2,3 일 동안 이 기능 을 실 현 했 는데 효과 가 좋 았 습 니 다.
우선 효과 도 를 살 펴 보 자.
这里写图片描述
사람 에 게 물고 기 를 가 르 치 는 것 보다 사람 에 게 물고 기 를 가 르 치 는 것 이 낫다.다음은 원 리 를 구체 적 으로 실현 하 는 교과 과정 이다.
1.실현 원리
원 리 는 사실 어렵 지 않다.간단하게 요약 하면 문 자 를 그 릴 때 표 시 된 문자 의 좌 표를 기록 하 는 것 이다(문자 의 왼쪽 위,오른쪽 아래 네 개의 점 좌 표를 기록 하 는 것).작용 은 바로 미끄럼 범 위 를 계산 하기 위 한 것 이다.이벤트 에 길 게 누 른 후 누 른 좌 표를 통 해 현재 표 시 된 문자 데이터 에서 점 의 좌표 에 따라 누 른 글 자 를 찾 고 길 게 누 른 후에 선택 한 위치 와 문 자 를 얻 을 수 있 습 니 다.미끄럼 선택 을 실행 할 때 손가락 이 미 끄 러 지 는 위치 좌표 에 따라 현재 표 시 된 텍스트 데이터 와 일치 하여 선택 한 범위 와 텍스트 를 확인 합 니 다.
2.구체 적 실현
봉인
조작 하기 편리 하도록 먼저 보 이 는 문자,보 이 는 줄 데 이 터 를 봉인 합 니 다.
ShowChar:

public class ShowChar {//        

  public char chardata ;//    
  public Boolean Selected =false;//         
  public Point TopLeftPosition = null;
  public Point TopRightPosition = null;
  public Point BottomLeftPosition = null;
  public Point BottomRightPosition = null;

  public float charWidth = 0;//    
  public int Index = 0;//      


}
ShowLine :

public class ShowLine {//      
  public List<ShowChar> CharsData = null;

  /**
   *@return
   *--------------------
   *TODO        
   *--------------------
   */
  public String getLineData(){
    String linedata = "";  
    if(CharsData==null||CharsData.size()==0) return linedata;
    for(ShowChar c:CharsData){
      linedata = linedata+c.chardata;
    }
    return linedata;
  }
}

설명:리더 가 데 이 터 를 한 줄 한 줄 로 표시 합 니 다.줄 마다 수량 이 불확실 한 문자 가 있 습 니 다.모든 문 자 는 자신의 정 보 를 가지 고 있 습 니 다.예 를 들 어 문자 너비,문자 가 데이터 집합 에서 의 아래 표지 등 입 니 다.그 릴 때 ShowLine 을 그 려 서 줄 마다 데 이 터 를 그립 니 다.
b.데이터 변환
그림 을 그리 기 전에,우 리 는 먼저 데 이 터 를 위 에 봉 인 된 형식 데이터 로 바 꾸 어 우리 가 사용 할 수 있 도록 해 야 한다.이거 어떻게 해 야 돼?문자열 을 한 줄 한 줄 의 데이터 로 바 꾸 고 모든 문자 의 문자 폭 을 측정 해 야 하기 때 문 입 니 다.그림 그리 기 에 익숙 하 다 면 시스템 에 paint.measuretext 가 문자 의 폭 을 측정 할 수 있다 는 것 을 알 수 있 을 것 입 니 다.이 를 통 해 문자 의 폭 을 측정 하 는 동시에 우리 가 원 하 는 줄 데이터 로 전환 할 수 있 습 니 다.
우선,들 어 오 는 문자열 을 줄 데이터 로 바 꿀 수 있 는 방법 을 쓰 십시오.

  /**
   *@param cs 
   *@param medsurewidth         
   *@param textpadding     
   *@param paint      
   *@return   cs       0,  null
   *--------------------
   *TODO 
   *--------------------
   */
  public static BreakResult BreakText(char[] cs, float medsurewidth, float textpadding, Paint paint) {  
    if(cs==null||cs.length==0){return null;}
    BreakResult breakResult = new BreakResult();    
    breakResult.showChars = new ArrayList<ShowChar>();
    float width = 0;

    for (int i = 0, size = cs.length; i < size; i++) {
      String mesasrustr = String.valueOf(cs[i]);
      float charwidth = paint.measureText(mesasrustr);

      if (width <= medsurewidth && (width + textpadding + charwidth) > medsurewidth) {
        breakResult.ChartNums = i;
        breakResult.IsFullLine = true;
        return breakResult;
      }

      ShowChar showChar = new ShowChar();
      showChar.chardata = cs[i];
      showChar.charWidth = charwidth;     
      breakResult.showChars.add(showChar);
      width += charwidth + textpadding;
    }

    breakResult.ChartNums = cs.length;
    return breakResult;
  }



public static BreakResult BreakText(String text, float medsurewidth, float textpadding, Paint paint) {
    if (TextUtils.isEmpty(text)) {
      int[] is = new int[2];
      is[0] = 0;
      is[1] = 0;
      return null;
    }
    return BreakText(text.toCharArray(), medsurewidth, textpadding, paint);

  }

설명:BreakResult 는 측정 결과 에 대한 간단 한 패키지 입 니 다.

public class BreakResult {

  public int ChartNums = 0;//       
  public Boolean IsFullLine = false;//      
  public List<ShowChar> showChars = null;//        

  public Boolean HasData() {
    return showChars != null && showChars.size() > 0;
  }
}

위의 작업 을 완성 한 후에 우 리 는 우리 가 표시 한 데 이 터 를 필요 한 데이터 로 바 꿀 수 있다.
다음은 테스트 에 표 시 된 문자열 입 니 다:

String TextData = "jEh      ,    ,    。      ,    。     , 、   ,     。           ,    ,      ,    ,     。      ,    、   。      ,    。    ,    ,     、        。         ,  、     ,    ,    ,      "
      +

  "        ,     。   ,      。       ,       ,    。   ,      ,     。  ,    。      ,    ,      ,      。      ,    ;     ,    ,        。    ,    。   ,     ,      。   ,      ;    ,    。    ,    。           ,      ,     ,        ,    。     ,    。      ,     ;        ,    。   、  、 、 、  、  、  、  、  、        ,  “   ”。     ,  “  ”。    ,        ,    。";

이 문자열 을 줄 데이터 로 바 꿔 야 합 니 다.데 이 터 를 초기 화 하 는 작업 입 니 다.다음은 데 이 터 를 초기 화 하 는 방법 initData 입 니 다.

List<ShowLine> mLinseData = null;

  private void initData(int viewwidth, int viewheight) {
    if (mLinseData == null) {
      //         
      mLinseData = BreakText(viewwidth, viewheight);
    }

  }

  private List<ShowLine> BreakText(int viewwidth, int viewheight) {
    List<ShowLine> showLines = new ArrayList<ShowLine>();
    while (TextData.length() > 0) {
      BreakResult breakResult = TextBreakUtil.BreakText(TextData, viewwidth, 0, mPaint);

      if (breakResult != null && breakResult.HasData()) {
        ShowLine showLine = new ShowLine();
        showLine.CharsData = breakResult.showChars;
        showLines.add(showLine);

      } else {
        break;
      }

      TextData = TextData.substring(breakResult.ChartNums);

    }

    int index = 0;
    for (ShowLine l : showLines) {
      for (ShowChar c : l.CharsData) {
        c.Index = index++;
      }
    }
    return showLines;
  }

initData 방법 을 호출 하면 TextData 의 데 이 터 를 표시 하 는 줄 데이터 Linedata 집합 mLinsedata 로 변환 할 수 있 습 니 다.
주의해 야 할 것 은 이 방법 을 사용 하려 면 컨트롤 의 길 이 를 알 아야 합 니 다.view 의 수명 주기 에 따라 onmeasures 에서 이 방법 을 사용 하여 초기 화 할 수 있 습 니 다.

@Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int viewwidth = getMeasuredWidth();
    int viewheight = getMeasuredHeight();
    initData(viewwidth, viewheight);
  }

데이터 전환 이 완 료 된 후에 우 리 는 데 이 터 를 한 줄 한 줄 그 려 야 한다.

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);  

    LineYPosition = TextHeight + LinePadding;//      y  
    for (ShowLine line : mLinseData) {
      DrawLineText(line, canvas);//     ,          
    }
  }

DrawLine Text 방법:

private void DrawLineText(ShowLine line, Canvas canvas) {
    canvas.drawText(line.getLineData(), 0, LineYPosition, mPaint);

    float leftposition = 0;
    float rightposition = 0;
    float bottomposition = LineYPosition + mPaint.getFontMetrics().descent;

    for (ShowChar c : line.CharsData) {
      rightposition = leftposition + c.charWidth;
      Point tlp = new Point();
      c.TopLeftPosition = tlp;
      tlp.x = (int) leftposition;
      tlp.y = (int) (bottomposition - TextHeight);

      Point blp = new Point();
      c.BottomLeftPosition = blp;
      blp.x = (int) leftposition;
      blp.y = (int) bottomposition;

      Point trp = new Point();
      c.TopRightPosition = trp;
      trp.x = (int) rightposition;
      trp.y = (int) (bottomposition - TextHeight);

      Point brp = new Point();
      c.BottomRightPosition = brp;
      brp.x = (int) rightposition;
      brp.y = (int) bottomposition;
      leftposition = rightposition;

    }
    LineYPosition = LineYPosition + TextHeight + LinePadding;
  }

실행 하 십시오.현재 표시 효 과 는 다음 과 같 습 니 다.
这里写图片描述
이 를 실현 한 후 에는 긴 선택 기능 과 미끄럼 선택 문자 기능 이 필요 하 다.어떻게 길 게 누 를 수 있 습 니까?스스로 쓰 면 될 것 입 니 다.다만 너무 번 거 로 울 뿐 입 니 다.그래서 우 리 는 시스템 이 제공 하 는 길 게 누 르 는 사건 을 빌 리 면 됩 니 다.내 가 실현 한 사고방식 은 이렇다.먼저 사건 처리 모델 을 네 가지 로 나눈다.

private enum Mode {

    Normal, //    
    PressSelectText,//      
    SelectMoveForward, //        
    SelectMoveBack//        
  }

어떠한 처리 도 하지 않 은 상태 에서 Normal 모드 입 니 다.제스처 가 발생 하면 Down 이벤트 가 촉발 되 고 현재 Down 의 좌 표를 기록 합 니 다.만약 에 사용자 가 계속 누 르 면 반드시 긴 이벤트 가 발생 하고 모드 는 PressSelectText 로 바 뀌 며 기 록 된 Down 의 좌 표를 통 해 데이터 집합 에서 현재 긴 문 자 를 찾 아 선택 한 문자 의 배경 을 그립 니 다.
생각 이 이 렇 습 니 다.그럼 하 세 요.우선 등록 장 은 이벤트 에 따라 이 사건 을 초기 화 합 니 다.

private void init() {
    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    mPaint.setTextSize(29);

    mTextSelectPaint = new Paint();
    mTextSelectPaint.setAntiAlias(true);
    mTextSelectPaint.setTextSize(19);
    mTextSelectPaint.setColor(TextSelectColor);

    mBorderPointPaint = new Paint();
    mBorderPointPaint.setAntiAlias(true);
    mBorderPointPaint.setTextSize(19);
    mBorderPointPaint.setColor(BorderPointColor);

    FontMetrics fontMetrics = mPaint.getFontMetrics();
    TextHeight = Math.abs(fontMetrics.ascent) + Math.abs(fontMetrics.descent);

    setOnLongClickListener(mLongClickListener);

  }

private OnLongClickListener mLongClickListener = new OnLongClickListener() {

    @Override
    public boolean onLongClick(View v) {

      if (mCurrentMode == Mode.Normal) {
        if (Down_X > 0 && Down_Y > 0) {//       ,     
          mCurrentMode = Mode.PressSelectText;
          postInvalidate();//  
        }
      }
      return false;
    }
  };

여기 다운X , Down_Y ; 초기 화 값 은 모두-1 입 니 다.다운 이 벤트 를 실행 하면 0 이상 일 것 입 니 다.Action 을 실행 하면up 이벤트,설정 값 을-1 로 방출 합 니 다.판단 을 위해 사용 할 뿐 입 니 다.
그리고 onDraw 에서 선택 한 문 자 를 판단 하고 그 려 야 합 니 다.

@Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    LineYPosition = TextHeight + LinePadding;//    y  
    for (ShowLine line : mLinseData) {
      DrawLineText(line, canvas);//    
    }

    if (mCurrentMode != Mode.Normal) {
      DrawSelectText(canvas);//        ,    
    }
  }

private void DrawSelectText(Canvas canvas) {
    if (mCurrentMode == Mode.PressSelectText) {
      DrawPressSelectText(canvas);//         
    } else if (mCurrentMode == Mode.SelectMoveForward) {//      
      DrawMoveSelectText(canvas);//            
    } else if (mCurrentMode == Mode.SelectMoveBack) {//      
      DrawMoveSelectText(canvas);//            
    }
  }
이 때 이벤트 가 길 게 실행 되면 mCurrentMode=Mode.PressSelectText 는 길 게 선택 한 문 자 를 그립 니 다.

     //         
private void DrawPressSelectText(Canvas canvas) {
    //               
    ShowChar p = DetectPressShowChar(Down_X, Down_Y);

    if (p != null) {//         
      FirstSelectShowChar = LastSelectShowChar = p;
      mSelectTextPath.reset();
      mSelectTextPath.moveTo(p.TopLeftPosition.x, p.TopLeftPosition.y);
      mSelectTextPath.lineTo(p.TopRightPosition.x, p.TopRightPosition.y);
      mSelectTextPath.lineTo(p.BottomRightPosition.x, p.BottomRightPosition.y);
      mSelectTextPath.lineTo(p.BottomLeftPosition.x, p.BottomLeftPosition.y);
      //      
      canvas.drawPath(mSelectTextPath, mTextSelectPaint);
      //          
      DrawBorderPoint(canvas);

    }
  }

클릭 점 이 있 는 문자 검사 방법:

  /**
   *@param down_X2
   *@param down_Y2
   *@return
   *--------------------
   *TODO                ,      null
   *--------------------
   */
  private ShowChar DetectPressShowChar(float down_X2, float down_Y2) {

    for (ShowLine l : mLinseData) {
      for (ShowChar c : l.CharsData) {
        if (down_Y2 > c.BottomLeftPosition.y) {
          break;//        
        }
        if (down_X2 >= c.BottomLeftPosition.x && down_X2 <= c.BottomRightPosition.x) {
          return c;
        }

      }
    }

    return null;
  }

기본적으로 이벤트 에 따라 작업 이 완료 되 었 습 니 다.우 리 는 문자 에 따라 효 과 를 봅 니 다.
선택 한 문 자 를 길 게 그 린 후에 우 리 는 좌우 의 지시 블록 에 따라 좌우 또는 상하 로 미 끄 러 지 며 문 자 를 선택해 야 합 니 다.조작 하기 편리 하도록 위로 미 끄 러 지 거나 아래로 미 끄 러 지 는 것 은 모두 미끄럼 범 위 를 제한 합 니 다.다음 과 같은 그림 입 니 다.
这里写图片描述
파란색 구역 은 손가락 을 누 른 후 터치 하여 미끄럼 을 허용 합 니 다.왼쪽 에 있 는 작은 파란색 구역 을 누 르 면 mCurrent Mode=Mode.selectMove Forward 는 위로 미 끄 러 지 며 텍스트 를 선택 할 수 있 습 니 다.바로 손가락 이 미 끄 러 지 는 좌표 가 노란색 구역 으로 미 끄 러 지 는 것 이 유효 합 니 다.오른쪽 에 있 는 작은 파란색 구역 을 누 르 면 mCurrent Mode==Mode.selectMoveBack,아래로 미 끄 러 지 며 텍스트 를 선택 할 수 있 습 니 다.바로 손가락 이 녹색 구역 으로 미 끄 러 지 는 것 이 유효 합 니 다.
선택 할 때 우 리 는 두 글자 만 기록 합 니 다.바로 선택 한 문자 의 시작 문자 와 끝 문자 입 니 다.

private ShowChar FirstSelectShowChar = null;
private ShowChar LastSelectShowChar = null;
길 게 누 르 면 문 자 를 선택 한 후:First SelectShow Char=LastSelectShow Char;
따라서 전체 과정 은 미 끄 러 질 때 왼쪽 파란색 구역 을 누 르 면 앞으로 미 끄 러 질 수 있 습 니 다.이때 mCurrent Mode=Mode.selectMoveForward 는 앞으로 미 끄 러 지 는 것 이 노란색 구역 에서 미 끄 러 지 는 것 입 니 다.이때 손가락 이 미 끄 러 지 는 좌표 에 따라 미 끄 러 진 FirstSelectShow Char 를 찾 은 다음 에 화면 을 새로 고 칠 수 있 습 니 다.아래로 미 끄 러 지 는 것 은 이치 에 맞다.
다음은 코드 구현:
먼저 ActionDown 에 서 는 아래로 미 끄 러 지 는 지 아래로 미 끄 러 지 는 지 판단 합 니 다.그렇지 않 으 면 리 셋 하여 선택 한 텍스트 를 원상 태 로 복원 합 니 다.

case MotionEvent.ACTION_DOWN:
      Down_X = Tounch_X;
      Down_Y = Tounch_Y;

      if (mCurrentMode != Mode.Normal) {
        Boolean isTrySelectMove = CheckIfTrySelectMove(Down_X, Down_Y);

        if (!isTrySelectMove) {//             ,       ,     
          mCurrentMode = Mode.Normal;
          invalidate();
        }
      }

      break;

미 끄 러 질 때 판단 합 니 다.위로 미 끄 러 지 는 경우 현재 미 끄 러 질 때의 First SelectShowChar 가 져 오기;아래로 미 끄 러 지면 현재 미 끄 러 질 때의 LastSelectShow Char 를 가 져 온 다음 화면 을 새로 고 칩 니 다.

case MotionEvent.ACTION_MOVE:
      if (mCurrentMode == Mode.SelectMoveForward) {
        if (CanMoveForward(event.getX(), event.getY())) {//          
          ShowChar firstselectchar = DetectPressShowChar(event.getX(), event.getY());//             
          if (firstselectchar != null) {
            FirstSelectShowChar = firstselectchar;
            invalidate();
          } 

        }

      } else if (mCurrentMode == Mode.SelectMoveBack) {

        if (CanMoveBack(event.getX(), event.getY())) {//                    
          ShowChar lastselectchar = DetectPressShowChar(event.getX(), event.getY());//             
          if (lastselectchar != null) {
            LastSelectShowChar = lastselectchar;
            invalidate();
          } 

        } 
      }

      break;

위로 미 끄 러 지 는 방법 판단:

private boolean CanMoveForward(float Tounchx, float Tounchy) {

    Path p = new Path();
    p.moveTo(LastSelectShowChar.TopRightPosition.x, LastSelectShowChar.TopRightPosition.y);
    p.lineTo(getWidth(), LastSelectShowChar.TopRightPosition.y);
    p.lineTo(getWidth(), 0);
    p.lineTo(0, 0);
    p.lineTo(0, LastSelectShowChar.BottomRightPosition.y);
    p.lineTo(LastSelectShowChar.BottomRightPosition.x, LastSelectShowChar.BottomRightPosition.y);
    p.lineTo(LastSelectShowChar.TopRightPosition.x, LastSelectShowChar.TopRightPosition.y);

    return computeRegion(p).contains((int) Tounchx, (int) Tounchy);
  }

아래로 미 끄 러 질 지 판단 하기:

private boolean CanMoveBack(float Tounchx, float Tounchy) {

    Path p = new Path();
    p.moveTo(FirstSelectShowChar.TopLeftPosition.x, FirstSelectShowChar.TopLeftPosition.y);
    p.lineTo(getWidth(), FirstSelectShowChar.TopLeftPosition.y);
    p.lineTo(getWidth(), getHeight());
    p.lineTo(0, getHeight());
    p.lineTo(0, FirstSelectShowChar.BottomLeftPosition.y);
    p.lineTo(FirstSelectShowChar.BottomLeftPosition.x, FirstSelectShowChar.BottomLeftPosition.y);
    p.lineTo(FirstSelectShowChar.TopLeftPosition.x, FirstSelectShowChar.TopLeftPosition.y);

    return computeRegion(p).contains((int) Tounchx, (int) Tounchy);
  }



private Region computeRegion(Path path) {
    Region region = new Region();
    RectF f = new RectF();
    path.computeBounds(f, true);
    region.setPath(path, new Region((int) f.left, (int) f.top, (int) f.right, (int) f.bottom));
    return region;
  }
제스처 처리 가 완료 되 었 습 니 다.나머지 는 ondraw 에서 mCurrent Mode==Mode.selectMove Forward 또는 mCurrent Mode==Mode.selectMoveBack 에서 선택 한 범위 배경 을 그립 니 다.

private void DrawSelectText(Canvas canvas) {
    if (mCurrentMode == Mode.PressSelectText) {
      DrawPressSelectText(canvas);//         
    } else if (mCurrentMode == Mode.SelectMoveForward) {//      
      DrawMoveSelectText(canvas);//            
    } else if (mCurrentMode == Mode.SelectMoveBack) {//      
      DrawMoveSelectText(canvas);//            
    }
  }


private void DrawMoveSelectText(Canvas canvas) {
    if (FirstSelectShowChar == null || LastSelectShowChar == null)     return;
    GetSelectData();//         ,         
    DrawSeletLines(canvas);//        
    DrawBorderPoint(canvas);//           
  }


private void DrawSeletLines(Canvas canvas) 
    DrawOaleSeletLinesBg(canvas);
  }

  private void DrawOaleSeletLinesBg(Canvas canvas) {//           
    for (ShowLine l : mSelectLines) {      
      if (l.CharsData != null && l.CharsData.size() > 0) {        
        ShowChar fistchar = l.CharsData.get(0);
        ShowChar lastchar = l.CharsData.get(l.CharsData.size() - 1);

        float fw = fistchar.charWidth;
        float lw = lastchar.charWidth;

        RectF rect = new RectF(fistchar.TopLeftPosition.x, fistchar.TopLeftPosition.y,
            lastchar.TopRightPosition.x, lastchar.BottomRightPosition.y);

        canvas.drawRoundRect(rect, fw / 2,
             TextHeight / 2, mTextSelectPaint);

      }
    }
  }

기본적으로 완성 되 었 으 니 운행 해 보 세 요.효과 가 괜 찮 습 니 다.
这里写图片描述
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기