Страницы

Поиск по вопросам

Показаны сообщения с ярлыком customview. Показать все сообщения
Показаны сообщения с ярлыком customview. Показать все сообщения

вторник, 31 декабря 2019 г.

Нестандартный вывод текста в TextView

#java #android #textview #customview


Необходимо вписать текст в TextView ограниченный кругом. пример 
Подскажите, в какую сторону копать
    


Ответы

Ответ 1



Написал код onDraw(Canvas canvas) своего класса наследника TextViewрешающий задачу для меня. Комментарии описываю последовательность действий: @Override protected void onDraw(Canvas canvas) { // круг, в который вписан текст canvas.drawCircle(mRadius, mRadius, mRadius, mGlowPaint); // высота строки float textHeight = mTextPaint.getFontSpacing(); // измеряем длинну текста float widthText = mTextPaint.measureText(mText); if(widthText <= mRadius*2) { // всего одна строка помещается - рисуем ее и выходим canvas.drawText(mText, mRadius, mRadius, mTextPaint); return; } // 1 Делим строку на два, сдвигаем к ближайшему пробелу. получаем индекс пробела, близкого к середине текста int centerSpace = calcCenterSpace(mText); // 2 Сначала рисуем текст вверх : String leftString = mText.substring(0, centerSpace); int cnt = 0; // количество прорисованных строк вверх float widthLine; int n; while (true) { // измеряем длинну строки widthLine = calcWidthLine(mRadius, textHeight * cnt++); // считаем сколько символов поместится в новой строке n = mTextPaint.breakText(leftString, false, widthLine, null); if (n == 0) // прерываем, так как строка слишком длинная break; // создаем строку int rest = leftString.length() - n; // проверка на последнюю строку if (rest != 0) { String s = leftString.substring(leftString.length() - n); // обрезаем до первого пробела int fixed = s.indexOf(' '); // создаем новую аккуратную строку s = s.substring(fixed + 1); // рисуем ее на высоте соответствующую данной строке canvas.drawText(s, mRadius, mRadius - textHeight * cnt + textHeight, mTextPaint); // сокращаем левый кусок текста на длинну отрисованной строки leftString = leftString.substring(0, leftString.length() - s.length()); } else { // последняя строка canvas.drawText(leftString, mRadius, mRadius - textHeight * cnt + textHeight, mTextPaint); break; } } // 3. Теперь рисуем вниз String rightString = mText.substring(centerSpace); cnt = 0; // количество прорисованных строк вверх while(true){ widthLine = calcWidthLine(mRadius, textHeight * cnt++); n = mTextPaint.breakText(rightString, false, widthLine, null); if (n == 0) // прерываем если весь текст не поместился break; int rest = rightString.length() - n; if(rest != 0) { String s = rightString.substring(0, n); int fixed = s.lastIndexOf(' '); s = s.substring(0, fixed); canvas.drawText(s, mRadius, mRadius + textHeight * cnt, mTextPaint); rightString = rightString.substring(s.length(), rightString.length()); } else { canvas.drawText(rightString, mRadius, mRadius + textHeight * cnt, mTextPaint); break; } } } private static int calcWidthLine(float r, float b){ double a = Math.sqrt(r * r - b * b); return (int)(2*a); } private static int calcCenterSpace(String text){ int index = text.length()/2; if(text.charAt(index) == ' ') return index; String subLeft = text.substring(0, index); int indexSpaceLeft = subLeft.lastIndexOf(' '); String subRight = text.substring(index + 2); int indexSpaceRight = subRight.indexOf(' '); if(index - indexSpaceLeft <= indexSpaceRight) return indexSpaceLeft; else return indexSpaceRight; } Алгоритм не идеален, на поставленную задачу выполняет. Еще в планах сделать растягивание строки. Оптимизация и недочеты приветствую. Вот скриншоты:

Ответ 2



Можно сделать кастомное view с LinearLayout, содержащий TextView различной длины. Предлагаю следующий алгоритм: Оценить размер круга, необходимый для размещения данного объема текста. Для выполнения этого шага нужно учесть остальные. Построить LinearLayout соответсвующей высоты и ширины. Разместить по строкам TextView различной длины. Длину определить по длине хорды на соответвующей высоте текста + высоты самой строки. Заполнить соответсвующие TextView текстом. Сложности могут возникнуть с переносом слов и обрывком строк не доходя до круга. Тут можно использовать свойство letter spacing: брать строки не больше заданной длины хорды и увеличивать letter-spacing, если строка меньше чем нужно. Поделиться реализацией с сообществом :-)

Ответ 3



Создайте в Drawable bg_circle.xml: Потом примените этот бекграунд к вашему TextView!

пятница, 27 декабря 2019 г.

Custom Arc Drawable ProgressBar

#customview #android #java


Необходимо сделать плавно заполняющийся ProgressBar следующего вида:


Есть ресурсы
background.png



progress.png

 

Пробовал делать так
main.xml





back_arc.xml



   
      
   



Результат получился немного не такой.

    


Ответы

Ответ 1



C помощью стандартного ProgressBar заявленную штуку не получилось сделать. Результат был достигнут с помощью RelativeLayout и наложением картинок друг на друга - снизу progress.png затем фон, который с помощью Rotate двигался в виде "заслонки" и поверх фон с дыркой под дугу (расписал, может кому-нибудь и пригодится).

вторник, 10 декабря 2019 г.

Циклическая прокрутка шкалы

#android #customview


Не могу разобраться, как можно реализовать циклическую прокрутку шкалы.
Рисую ее в customview:

public void onDraw(Canvas canvas){
startingPoint = mainPoint;
counter = 0;
for (int i = 1;; i++) {
    if (startingPoint > screenSize) {
        break;
    }

    if(i % 4 == 0) {
        size = scaleHeight / 4;
        counter = counter + 1;
    } else {
        if(i % 2 == 0) {
            size = scaleHeight / 8;
        } else {
            size = scaleHeight / 16;
        }
    }
    canvas.drawLine(startingPoint, endPoint - size, startingPoint, endPoint, rulerPaint);

    if (i % 4 == 0) {
        String c = Integer.toString(counter);
        canvas.drawText(c, startingPoint, endPoint - (size + 20), textPaint);
    }

    startingPoint = startingPoint + pxmm;
}


Ограничиваю ее до одиннадцати длинных делений:


Но мне нужна бесконечная шкала:



Допустим, можно убрать проверку значения counter в onDraw методе, но как это скажется
на производительности? Даже если ставлю его значение 1000, уже заметны "подлагивания"
при скроллинге.
Вот мой onScroll:

public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        mainPoint = mainPoint - distanceX;
        invalidate();
        return true;
}


И onMeasure:

protected void onMeasure(int w, int h) {
        DisplayMetrics metrics = new DisplayMetrics();    ((Activity)getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
        h = metrics.heightPixels / 5;
        w = metrics.widthPixels;

        setMeasuredDimension(w, h);

        scaleHeight = h;
        scaleWidth = w;
        screenSize = w;
        endPoint = h;
        midScreenPoint = w / 2;
        pxmm = screenSize / 28;
    }


Каким образом реализуются подобные задачи? 
    


Ответы

Ответ 1



Надо рисовать шкалу от начала экрана до его конца. Т.е. цикл должен быть не по шкале, а по ширине экрана. А в цикле уже вести счетчик по шкале, как только он достигнет 11, обнулять. Такой подход позволит легко рисовать и шкалу со смещением, просто присваиваете счетчику шкалы это смещение вначале и готово. Скроллинг будет не нужным.

Ответ 2



В текущей реализации происходит следующее: mainPoint инициализируется нулем. в onScroll mainPoint изменяется на значение -distanceX, соответсвенно при скролле в положительное направление по оси X значение mainPoint будет уменьшатся. То есть в какой-то момент значение mainPoint будет предположим -1000. Но при этом значение, возвращаемое из getScrollX() будет как раз таки 1000, поскольку View будет проскроллено как раз на это значение. Метод onDraw считает, что координаты View следующие: (l, t, r, b) == (0, 0, getMeasuredWidth(), getMeasuredHeight()) и цикл в onDraw будет отрисовывать линейку от значения -1000 до getMeasuredWidth() каждый раз - из за этого и возникают лаги и подергивания. Чтобы их не было нужно отрисовывать линейку не от -1000, а от 0. Собственно видимо это и имел в виду Евгений. Как можно исправить: В onDraw получать значение из getScrollX()(либо брать тот же mainPoint) и рассчитывать метку линейки в нулевой координате. В моем примере с 1000 и значением pxmm предположим 24 получается, что в нулевой координате метки линейки будет 41,6(6). Соответственно метка линейки 41 и 41,5 будут за пределом видимости, а значение 42 как раз будет отрисовано со сдвигом 42 * 24 - 1000 = 8 пикселов от левого края экрана. Единственное что - надо отрисовывать немного не с нуля, а с отрицательного значения, потому что метка может быть за пределами экрана, а сама цифра может частично уже попадать на экран. В принципе, чтобы не особо заморачиваться, можно начинать отрисовывать со значения 41 * 24 - 1000 = -16 пикселов, то есть со сдвигом на одно деление линейки в левую сторону экрана.

воскресенье, 8 декабря 2019 г.

обтекание изображения текстом в EditText

#java #android #android_edittext #android_layout #customview


Справа от ImageView в EditText должен вводиться текст. Если вдруг окажется что текст
заполнил все пространство справа от ImageView, нужно чтобы текст продолжал вводиться
под ImageView начиная от самого (левого) края экрана устройства. Схематически это представлено
на прикрепленном рисунке. 


Сразу скажу что подобный вариант чудесно работает с TextView, но не хочет сотрудничать
с EditText. Прошу не оставаться равнодушными и подсказать хотя бы направление решения
моей проблемы.
    


Ответы

Ответ 1



После нескольких экспериментов и изучения EditText/TextView оказалось, что в TextView (в котором собственно и задано все необходимое для редактирования текста) есть ошибки (или недоделки) из-за которых он при отрисовке курсора и выделения не принимает во внимание span текста. В результате текст в EditText при использовании LeadingMarginSpan отрисовывается правильно, а выделение и курсор ведут себя так, как будто текст занимает прямоугольник (за исключением первой строки). Чтобы это реализовать надо делать свой кастомный TextView и править в нем баги доделывать недоделки. Учитывая, что TextView содержит порядка 10000 строк кода это будет непросто.

суббота, 16 марта 2019 г.

Custom Arc Drawable ProgressBar

Необходимо сделать плавно заполняющийся ProgressBar следующего вида:
Есть ресурсы background.png

progress.png

Пробовал делать так main.xml

back_arc.xml

Результат получился немного не такой.


Ответ

C помощью стандартного ProgressBar заявленную штуку не получилось сделать. Результат был достигнут с помощью RelativeLayout и наложением картинок друг на друга - снизу progress.png затем фон, который с помощью Rotate двигался в виде "заслонки" и поверх фон с дыркой под дугу (расписал, может кому-нибудь и пригодится).

вторник, 25 декабря 2018 г.

Нестандартный вывод текста в TextView

Необходимо вписать текст в TextView ограниченный кругом. пример Подскажите, в какую сторону копать


Ответ

Написал код onDraw(Canvas canvas) своего класса наследника TextViewрешающий задачу для меня. Комментарии описываю последовательность действий:
@Override protected void onDraw(Canvas canvas) { // круг, в который вписан текст canvas.drawCircle(mRadius, mRadius, mRadius, mGlowPaint); // высота строки float textHeight = mTextPaint.getFontSpacing(); // измеряем длинну текста float widthText = mTextPaint.measureText(mText); if(widthText <= mRadius*2) { // всего одна строка помещается - рисуем ее и выходим canvas.drawText(mText, mRadius, mRadius, mTextPaint); return; } // 1 Делим строку на два, сдвигаем к ближайшему пробелу. получаем индекс пробела, близкого к середине текста int centerSpace = calcCenterSpace(mText); // 2 Сначала рисуем текст вверх : String leftString = mText.substring(0, centerSpace); int cnt = 0; // количество прорисованных строк вверх float widthLine; int n; while (true) { // измеряем длинну строки widthLine = calcWidthLine(mRadius, textHeight * cnt++); // считаем сколько символов поместится в новой строке n = mTextPaint.breakText(leftString, false, widthLine, null); if (n == 0) // прерываем, так как строка слишком длинная break; // создаем строку int rest = leftString.length() - n; // проверка на последнюю строку if (rest != 0) { String s = leftString.substring(leftString.length() - n); // обрезаем до первого пробела int fixed = s.indexOf(' '); // создаем новую аккуратную строку s = s.substring(fixed + 1); // рисуем ее на высоте соответствующую данной строке canvas.drawText(s, mRadius, mRadius - textHeight * cnt + textHeight, mTextPaint); // сокращаем левый кусок текста на длинну отрисованной строки leftString = leftString.substring(0, leftString.length() - s.length()); } else { // последняя строка canvas.drawText(leftString, mRadius, mRadius - textHeight * cnt + textHeight, mTextPaint); break; } } // 3. Теперь рисуем вниз String rightString = mText.substring(centerSpace); cnt = 0; // количество прорисованных строк вверх while(true){ widthLine = calcWidthLine(mRadius, textHeight * cnt++); n = mTextPaint.breakText(rightString, false, widthLine, null); if (n == 0) // прерываем если весь текст не поместился break; int rest = rightString.length() - n; if(rest != 0) { String s = rightString.substring(0, n); int fixed = s.lastIndexOf(' '); s = s.substring(0, fixed); canvas.drawText(s, mRadius, mRadius + textHeight * cnt, mTextPaint); rightString = rightString.substring(s.length(), rightString.length()); } else { canvas.drawText(rightString, mRadius, mRadius + textHeight * cnt, mTextPaint); break; } } }
private static int calcWidthLine(float r, float b){ double a = Math.sqrt(r * r - b * b); return (int)(2*a); }
private static int calcCenterSpace(String text){ int index = text.length()/2; if(text.charAt(index) == ' ') return index; String subLeft = text.substring(0, index); int indexSpaceLeft = subLeft.lastIndexOf(' '); String subRight = text.substring(index + 2); int indexSpaceRight = subRight.indexOf(' '); if(index - indexSpaceLeft <= indexSpaceRight) return indexSpaceLeft; else return indexSpaceRight; }
Алгоритм не идеален, на поставленную задачу выполняет. Еще в планах сделать растягивание строки. Оптимизация и недочеты приветствую. Вот скриншоты:

понедельник, 15 октября 2018 г.

Циклическая прокрутка шкалы

Не могу разобраться, как можно реализовать циклическую прокрутку шкалы. Рисую ее в customview:
public void onDraw(Canvas canvas){ startingPoint = mainPoint; counter = 0; for (int i = 1;; i++) { if (startingPoint > screenSize) { break; }
if(i % 4 == 0) { size = scaleHeight / 4; counter = counter + 1; } else { if(i % 2 == 0) { size = scaleHeight / 8; } else { size = scaleHeight / 16; } } canvas.drawLine(startingPoint, endPoint - size, startingPoint, endPoint, rulerPaint);
if (i % 4 == 0) { String c = Integer.toString(counter); canvas.drawText(c, startingPoint, endPoint - (size + 20), textPaint); }
startingPoint = startingPoint + pxmm; }
Ограничиваю ее до одиннадцати длинных делений:
Но мне нужна бесконечная шкала:

Допустим, можно убрать проверку значения counter в onDraw методе, но как это скажется на производительности? Даже если ставлю его значение 1000, уже заметны "подлагивания" при скроллинге. Вот мой onScroll:
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { mainPoint = mainPoint - distanceX; invalidate(); return true; }
И onMeasure:
protected void onMeasure(int w, int h) { DisplayMetrics metrics = new DisplayMetrics(); ((Activity)getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics); h = metrics.heightPixels / 5; w = metrics.widthPixels;
setMeasuredDimension(w, h);
scaleHeight = h; scaleWidth = w; screenSize = w; endPoint = h; midScreenPoint = w / 2; pxmm = screenSize / 28; }
Каким образом реализуются подобные задачи?


Ответ

Надо рисовать шкалу от начала экрана до его конца. Т.е. цикл должен быть не по шкале, а по ширине экрана. А в цикле уже вести счетчик по шкале, как только он достигнет 11, обнулять.
Такой подход позволит легко рисовать и шкалу со смещением, просто присваиваете счетчику шкалы это смещение вначале и готово. Скроллинг будет не нужным.

пятница, 12 октября 2018 г.

обтекание изображения текстом в EditText

Справа от ImageView в EditText должен вводиться текст. Если вдруг окажется что текст заполнил все пространство справа от ImageView, нужно чтобы текст продолжал вводиться под ImageView начиная от самого (левого) края экрана устройства. Схематически это представлено на прикрепленном рисунке.
Сразу скажу что подобный вариант чудесно работает с TextView, но не хочет сотрудничать с EditText. Прошу не оставаться равнодушными и подсказать хотя бы направление решения моей проблемы.


Ответ

После нескольких экспериментов и изучения EditText/TextView оказалось, что в TextView (в котором собственно и задано все необходимое для редактирования текста) есть ошибки (или недоделки) из-за которых он при отрисовке курсора и выделения не принимает во внимание span текста.
В результате текст в EditText при использовании LeadingMarginSpan отрисовывается правильно, а выделение и курсор ведут себя так, как будто текст занимает прямоугольник (за исключением первой строки).
Чтобы это реализовать надо делать свой кастомный TextView и править в нем баги доделывать недоделки. Учитывая, что TextView содержит порядка 10000 строк кода это будет непросто.