Мне нужно сделать макет таким образом, чтобы Textview начинал с Right of Imageview. Если текст длинный, следует продолжить ниже imageview [duplicate]

Что делает ** (двойная звезда) и * (звезда) для параметров

blockquote>

Они позволяют определять функции для принятия и для пользователей проходить любое число аргументов, positional (*) и ключевое слово (**).

Определяющие функции

*args допускают любое количество необязательных позиционных аргументов (параметров), который будет присвоен кортежу с именем args.

**kwargs допускает любое количество необязательных аргументов (параметров) ключевого слова, которые будут находиться в имени dict kwargs.

Вы можете (и должны) выбрать любое подходящее имя , но если целью является аргумент неспецифической семантики, args и kwargs являются стандартными именами.

Расширение, Передача любого количества аргументов

Вы также могут использовать *args и **kwargs для передачи параметров из списков (или любого итеративного) и dicts (или любого сопоставления) соответственно.

Функция, получающая параметры, не должна знать, что они расширяются.

Например, xrange Python 2 явно не ожидает *args, но поскольку в качестве аргументов он принимает 3 целых числа:

>>> x = xrange(3) # create our *args - an iterable of 3 integers
>>> xrange(*x)    # expand here
xrange(0, 2, 2)

В качестве другого примера мы можем использовать расширение dict в str.format:

>>> foo = 'FOO'
>>> bar = 'BAR'
>>> 'this is foo, {foo} and bar, {bar}'.format(**locals())
'this is foo, FOO and bar, BAR'

Новое в Python 3: Определение функций только с ключевыми аргументами

После [f0] ключевого слова можно использовать только - например, здесь kwarg2 должен быть задан как аргумент ключевого слова - не позиционно:

def foo(arg, kwarg=None, *args, kwarg2=None, **kwargs): 
    return arg, kwarg, args, kwarg2, kwargs

Использование:

>>> foo(1,2,3,4,5,kwarg2='kwarg2', bar='bar', baz='baz')
(1, 2, (3, 4, 5), 'kwarg2', {'bar': 'bar', 'baz': 'baz'})

Также можно использовать *

def foo(arg, kwarg=None, *, kwarg2=None, **kwargs): 
    return arg, kwarg, kwarg2, kwargs

Здесь kwarg2 снова должен быть явно именованным аргументом ключевого слова:

>>> foo(1,2,kwarg2='kwarg2', foo='foo', bar='bar')
(1, 2, 'kwarg2', {'foo': 'foo', 'bar': 'bar'})

И мы больше не можем принимать неограниченные позиционные аргументы, потому что у нас нет *args*:

>>> foo(1,2,3,4,5, kwarg2='kwarg2', foo='foo', bar='bar')
Traceback (most recent call last):
  File "", line 1, in 
TypeError: foo() takes from 1 to 2 positional arguments 
    but 5 positional arguments (and 1 keyword-only argument) were given

Опять же, здесь мы требуем, чтобы kwarg указывался по имени, а не по положению :

def bar(*, kwarg=None): 
    return kwarg

В этом примере мы видим, что если мы попытаемся передать kwarg позиционно, мы получим ошибку:

>>> bar('kwarg')
Traceback (most recent call last):
  File "", line 1, in 
TypeError: bar() takes 0 positional arguments but 1 was given

We должен явно передать параметр kwarg в качестве аргумента ключевого слова.

>>> bar(kwarg='kwarg')
'kwarg'

Совместимые с Python демки

*args (обычно называемые «star-args») и **kwargs (звезды могут подразумеваться, говоря «kwargs», но быть явным с «двойными звездами») являются общими идиомами Python для использования нот * и **. Эти конкретные имена переменных не требуются (например, вы могли бы использовать *foos и **bars), но отклонение от конвенции может вызвать возмущение у ваших коллег-программистов Python.

Обычно мы используем их, когда мы не знаем, что получит наша функция, или сколько аргументов мы можем передавать, а иногда даже при наименовании каждой переменной отдельно получилось бы очень грязное и избыточное (но это это случай, когда обычно явный лучше, чем неявный).

Пример 1

Следующая функция описывает, как их можно использовать и демонстрирует поведение. Обратите внимание, что именованный аргумент b будет потребляться вторым аргументом position before:

def foo(a, b=10, *args, **kwargs):
    '''
    this function takes required argument a, not required keyword argument b
    and any number of unknown positional arguments and keyword arguments after
    '''
    print('a is a required argument, and its value is {0}'.format(a))
    print('b not required, its default value is 10, actual value: {0}'.format(b))
    # we can inspect the unknown arguments we were passed:
    #  - args:
    print('args is of type {0} and length {1}'.format(type(args), len(args)))
    for arg in args:
        print('unknown arg: {0}'.format(arg))
    #  - kwargs:
    print('kwargs is of type {0} and length {1}'.format(type(kwargs),
                                                        len(kwargs)))
    for kw, arg in kwargs.items():
        print('unknown kwarg - kw: {0}, arg: {1}'.format(kw, arg))
    # But we don't have to know anything about them 
    # to pass them to other functions.
    print('Args or kwargs can be passed without knowing what they are.')
    # max can take two or more positional args: max(a, b, c...)
    print('e.g. max(a, b, *args) \n{0}'.format(
      max(a, b, *args))) 
    kweg = 'dict({0})'.format( # named args same as unknown kwargs
      ', '.join('{k}={v}'.format(k=k, v=v) 
                             for k, v in sorted(kwargs.items())))
    print('e.g. dict(**kwargs) (same as {kweg}) returns: \n{0}'.format(
      dict(**kwargs), kweg=kweg))

Мы можем проверить онлайн-справку для подписи функции с помощью help(foo), которая сообщает нам

foo(a, b=10, *args, **kwargs)

Назовем эту функцию с помощью foo(1, 2, 3, 4, e=5, f=6, g=7)

, который печатает:

a is a required argument, and its value is 1
b not required, its default value is 10, actual value: 2
args is of type  and length 2
unknown arg: 3
unknown arg: 4
kwargs is of type  and length 3
unknown kwarg - kw: e, arg: 5
unknown kwarg - kw: g, arg: 7
unknown kwarg - kw: f, arg: 6
Args or kwargs can be passed without knowing what they are.
e.g. max(a, b, *args) 
4
e.g. dict(**kwargs) (same as dict(e=5, f=6, g=7)) returns: 
{'e': 5, 'g': 7, 'f': 6}

Пример 2

Мы также можем назвать это с помощью другого функция, в которую мы просто предоставляем a:

def bar(a):
    b, c, d, e, f = 2, 3, 4, 5, 6
    # dumping every local variable into foo as a keyword argument 
    # by expanding the locals dict:
    foo(**locals()) 

bar(100) prints:

a is a required argument, and its value is 100
b not required, its default value is 10, actual value: 2
args is of type  and length 0
kwargs is of type  and length 4
unknown kwarg - kw: c, arg: 3
unknown kwarg - kw: e, arg: 5
unknown kwarg - kw: d, arg: 4
unknown kwarg - kw: f, arg: 6
Args or kwargs can be passed without knowing what they are.
e.g. max(a, b, *args) 
100
e.g. dict(**kwargs) (same as dict(c=3, d=4, e=5, f=6)) returns: 
{'c': 3, 'e': 5, 'd': 4, 'f': 6}

Пример 3: практическое использование в декораторах

Хорошо, так что, возможно, мы еще не видим утилиту. Итак, представьте, что у вас есть несколько функций с избыточным кодом до и / или после дифференцирующего кода. Следующие именованные функции являются просто псевдокодом для иллюстративных целей.

def foo(a, b, c, d=0, e=100):
    # imagine this is much more code than a simple function call
    preprocess() 
    differentiating_process_foo(a,b,c,d,e)
    # imagine this is much more code than a simple function call
    postprocess()

def bar(a, b, c=None, d=0, e=100, f=None):
    preprocess()
    differentiating_process_bar(a,b,c,d,e,f)
    postprocess()

def baz(a, b, c, d, e, f):
    ... and so on

Мы могли бы обрабатывать это по-другому, но мы можем, конечно, извлечь избыточность с помощью декоратора, и поэтому наш приведенный ниже пример демонстрирует, как *args и **kwargs могут быть очень полезными:

def decorator(function):
    '''function to wrap other functions with a pre- and postprocess'''
    @functools.wraps(function) # applies module, name, and docstring to wrapper
    def wrapper(*args, **kwargs):
        # again, imagine this is complicated, but we only write it once!
        preprocess()
        function(*args, **kwargs)
        postprocess()
    return wrapper

И теперь каждая завернутая функция может быть написана гораздо более лаконично, поскольку мы учли избыточность:

@decorator
def foo(a, b, c, d=0, e=100):
    differentiating_process_foo(a,b,c,d,e)

@decorator
def bar(a, b, c=None, d=0, e=100, f=None):
    differentiating_process_bar(a,b,c,d,e,f)

@decorator
def baz(a, b, c=None, d=0, e=100, f=None, g=None):
    differentiating_process_baz(a,b,c,d,e,f, g)

@decorator
def quux(a, b, c=None, d=0, e=100, f=None, g=None, h=None):
    differentiating_process_quux(a,b,c,d,e,f,g,h)

И, разглаживая наш код, который позволяет нам делать *args и **kwargs, мы сокращаем строки кода, улучшаем читаемость и ремонтопригодность и имеем единственные канонические места для логики нашей программы. Если нам нужно изменить какую-либо часть этой структуры, у нас есть одно место, в которое можно внести каждое изменение.

57
задан silverburgh 12 February 2010 в 01:12
поделиться

8 ответов

Этот вопрос кажется таким же, как мой вопрос Как заполнить пустые пространства содержимым ниже изображения в android

Я нашел решение, используя библиотеку flowtext, найдите первый ответ это может помочь вам до сих пор

2
ответ дан Community 25 August 2018 в 14:58
поделиться

Я могу предложить более удобный конструктор для класса MyLeadingMarginSpan2

    MyLeadingMarginSpan2(Context cc,int textSize,int height,int width) {                
    int pixelsInLine=(int) (textSize*cc.getResources().getDisplayMetrics().scaledDensity);              
    if (pixelsInLine>0 && height>0) {
        this.lines=height/pixelsInLine;          
    } else  {
        this.lines=0;
    }
    this.margin=width; 
}
0
ответ дан Evgeny Karavashkin 25 August 2018 в 14:58
поделиться
  • 1
    Привет, Евгений, как установить text flow around image для изображения, расположенного в правой части экрана? Ответ высоко оценен. – Ramona 8 August 2017 в 13:30

Ответы Vorrtex и Ronen работают на меня, за исключением одной детали. После обтекания текста вокруг изображения был странный «отрицательный» край под изображением и на противоположной стороне. Я понял, что при установке диапазона на SpannableString я изменил

ss.setSpan(new MyLeadingMarginSpan2(lines, width), 0, ss.length(), 0);

на

ss.setSpan(new MyLeadingMarginSpan2(lines, width), 0, lines, 0);

, который остановил интервал после изображения. Может быть, не нужно во всех случаях, но думал, что я поделюсь.

2
ответ дан johosher 25 August 2018 в 14:58
поделиться

Ответ vorrtex не сработал для меня, но я взял много от него и придумал свое решение. Вот он:

package ie.moses.keepitlocal.util;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.IntRange;
import android.text.Layout;
import android.text.style.LeadingMarginSpan;
import android.view.View;
import android.widget.TextView;

import ie.moses.keepitlocal.util.MeasurementUtils;
import ie.moses.keepitlocal.util.TextUtils;

import static com.google.common.base.Preconditions.checkArgument;

public class WrapViewSpan implements LeadingMarginSpan.LeadingMarginSpan2 {

    private final Context _context;
    private final int _lineCount;
    private int _leadingMargin;
    private int _padding;

    public WrapViewSpan(View wrapeeView, TextView wrappingView) {
        this(wrapeeView, wrappingView, 0);
    }

    /**
     * @param padding Padding in DIP.
     */
    public WrapViewSpan(View wrapeeView, TextView wrappingView, @IntRange(from = 0) int padding) {
        _context = wrapeeView.getContext();
        setPadding(padding);

        int wrapeeHeight = wrapeeView.getHeight();
        float lineHeight = TextUtils.getLineHeight(wrappingView);

        int lineCnt = 0;
        float linesHeight = 0F;
        while ((linesHeight += lineHeight) <= wrapeeHeight) {
            lineCnt++;
        }

        _lineCount = lineCnt;
        _leadingMargin = wrapeeView.getWidth();
    }

    public void setPadding(@IntRange(from = 0) int paddingDp) {
        checkArgument(paddingDp >= 0, "padding cannot be negative");
        _padding = (int) MeasurementUtils.dpiToPixels(_context, paddingDp);
    }

    @Override
    public int getLeadingMarginLineCount() {
        return _lineCount;
    }

    @Override
    public int getLeadingMargin(boolean first) {
        if (first) {
            return _leadingMargin + _padding;
        } else {
            return _padding;
        }
    }

    @Override
    public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline,
                                  int bottom, CharSequence text, int start, int end,
                                  boolean first, Layout layout) {

    }

}

и в моем фактическом классе, где используется диапазон:

ViewTreeObserver headerViewTreeObserver = _headerView.getViewTreeObserver();
headerViewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        String descriptionText = _descriptionView.getText().toString();
        SpannableString spannableDescriptionText = new SpannableString(descriptionText);
        LeadingMarginSpan wrapHeaderSpan = new WrapViewSpan(_headerView, _descriptionView, 12);
        spannableDescriptionText.setSpan(
                wrapHeaderSpan,
                0,
                spannableDescriptionText.length(),
                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        );
        _descriptionView.setText(spannableDescriptionText);
        ViewTreeObserver headerViewTreeObserver = _headerView.getViewTreeObserver();
        headerViewTreeObserver.removeOnGlobalLayoutListener(this);
    }
});

Мне нужен глобальный приемник макета, чтобы получить правильные значения для getWidth() и getHeight().

Вот результат:

0
ответ дан Moses 25 August 2018 в 14:58
поделиться

В настоящее время вы можете использовать библиотеку: https://github.com/deano2390/FlowTextView . Например:

<uk.co.deanwild.flowtextview.FlowTextView
    android:id="@+id/ftv"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" >

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true"
            android:padding="10dip"
            android:src="@drawable/android"/>
</uk.co.deanwild.flowtextview.FlowTextView>
2
ответ дан Nikita Shaposhnik 25 August 2018 в 14:58
поделиться

Теперь это возможно, но только для телефонов с версией выше или равной 2.2 с использованием интерфейса android.text.style.LeadingMarginSpan.LeadingMarginSpan2, доступного в API 8.

Вот статья , но не на английском языке, но вы можете загрузить исходный код примера непосредственно из здесь .

Если вы хотите, чтобы ваше приложение совместимо со старыми устройствами, вы можете отобразить разная компоновка без плавающего текста. Вот пример:

Макет (по умолчанию для более старых версий будет изменен программно для более новых версий)

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content">
    <ImageView 
            android:id="@+id/thumbnail_view"
            android:src="@drawable/icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    <TextView android:id="@+id/message_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@id/thumbnail_view"
            android:textSize="18sp"
            android:text="@string/text" />
</RelativeLayout>

Вспомогательный класс

class FlowTextHelper {

    private static boolean mNewClassAvailable;

    static {
        if (Integer.parseInt(Build.VERSION.SDK) >= 8) { // Froyo 2.2, API level 8
           mNewClassAvailable = true;
        }
    }

    public static void tryFlowText(String text, View thumbnailView, TextView messageView, Display display){
        // There is nothing I can do for older versions, so just return
        if(!mNewClassAvailable) return;

        // Get height and width of the image and height of the text line
        thumbnailView.measure(display.getWidth(), display.getHeight());
        int height = thumbnailView.getMeasuredHeight();
        int width = thumbnailView.getMeasuredWidth();
        float textLineHeight = messageView.getPaint().getTextSize();

        // Set the span according to the number of lines and width of the image
        int lines = (int)FloatMath.ceil(height / textLineHeight);
        //For an html text you can use this line: SpannableStringBuilder ss = (SpannableStringBuilder)Html.fromHtml(text);
        SpannableString ss = new SpannableString(text);
        ss.setSpan(new MyLeadingMarginSpan2(lines, width), 0, ss.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        messageView.setText(ss);

        // Align the text with the image by removing the rule that the text is to the right of the image
        RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)messageView.getLayoutParams();
        int[]rules = params.getRules();
        rules[RelativeLayout.RIGHT_OF] = 0;
    }
}

Класс myLeadingMarginSpan2 (обновлен для поддержки API 21)

public class MyLeadingMarginSpan2 implements LeadingMarginSpan2 {
    private int margin;
    private int lines;
    private boolean wasDrawCalled = false;
    private int drawLineCount = 0;

    public MyLeadingMarginSpan2(int lines, int margin) {
        this.margin = margin;
        this.lines = lines;
    }

    @Override
    public int getLeadingMargin(boolean first) {
        boolean isFirstMargin = first;
        // a different algorithm for api 21+
        if (Build.VERSION.SDK_INT >= 21) {
            this.drawLineCount = this.wasDrawCalled ? this.drawLineCount + 1 : 0;
            this.wasDrawCalled = false;
            isFirstMargin = this.drawLineCount <= this.lines;
        }

        return isFirstMargin ? this.margin : 0;
    }

    @Override
    public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, CharSequence text, int start, int end, boolean first, Layout layout) {
        this.wasDrawCalled = true;
    }

    @Override
    public int getLeadingMarginLineCount() {
        return this.lines;
    }
}

Пример использования

ImageView thumbnailView = (ImageView) findViewById(R.id.thumbnail_view);
TextView messageView = (TextView) findViewById(R.id.message_view);
String text = getString(R.string.text);

Display display = getWindowManager().getDefaultDisplay();
FlowTextHelper.tryFlowText(text, thumbnailView, messageView, display);

Так выглядит приложение на устройстве Android 2.2: Android 2.2 the text flows around the image [/g2]

И это для устройства Android 2.1:

Android 2.1 the text is situated near the image [/g3]

60
ответ дан Parker 25 August 2018 в 14:58
поделиться
  • 1
    Вместо трюка Class.forName вы можете использовать простое условие: if (Build.VERSION.SDK_INT & lt; Build.VERSION_CODES.FROYO) {... – Wojciech Górski 30 August 2012 в 16:29
  • 2
    Я также использую это. Но когда данные с тегами Html не поддерживаются для Html.fromHtml (html-контент), пожалуйста, помогите мне, мне нужно показать список с адаптером wrapText, как указано выше – Harsha 27 November 2014 в 12:13
  • 3
    @Harsha Метод Html.fromHtml не будет работать с каким-либо html, он поддерживает только простой html с несколькими тегами. – vorrtex 14 December 2014 в 08:39
  • 4
    Большая работа, я потратил свой день почти ... Спасибо тонну !! – Palak Darji 12 March 2015 в 11:13
  • 5
    Это также добавляет правильный запас, хотя для линий, проходящих мимо изображения (так что текст никогда не проходит полностью). Любая идея, как исправить эту ошибку? – Parker 1 April 2016 в 02:43

«Но я не уверен, что он подразумевает, выполнив мою собственную версию TextView?»

Он означает, что вы можете расширить класс android.widget.TextView (или Canvas или какой-либо другой визуализируемой поверхности) и реализовать свою собственную переопределяющую версию, которая позволяет встроенным изображениям с текстом, текущим вокруг них.

Это может быть довольно много работы в зависимости от того, насколько вы это делаете.

1
ответ дан Peter vdL 25 August 2018 в 14:58
поделиться

Это улучшение для FlowTextHelper (из ответа vorrtex). Я добавил возможность добавить дополнительное дополнение между текстом и изображением и улучшить расчет строки, чтобы также учитывать отступы. Наслаждайтесь!

public class FlowTextHelper {
   private static boolean mNewClassAvailable;

   /* class initialization fails when this throws an exception */
   static {
       try {
           Class.forName("android.text.style.LeadingMarginSpan$LeadingMarginSpan2");
           mNewClassAvailable = true;
       } catch (Exception ex) {
           mNewClassAvailable = false;
       }
   }

   public static void tryFlowText(String text, View thumbnailView, TextView messageView, Display display, int addPadding){
       // There is nothing I can do for older versions, so just return
       if(!mNewClassAvailable) return;



       // Get height and width of the image and height of the text line
        thumbnailView.measure(display.getWidth(), display.getHeight());
        int height = thumbnailView.getMeasuredHeight();
        int width = thumbnailView.getMeasuredWidth() + addPadding;
        messageView.measure(width, height); //to allow getTotalPaddingTop
        int padding = messageView.getTotalPaddingTop();
        float textLineHeight = messageView.getPaint().getTextSize();

        // Set the span according to the number of lines and width of the image
        int lines =  (int)Math.round((height - padding) / textLineHeight);
        SpannableString ss = new SpannableString(text);
        //For an html text you can use this line: SpannableStringBuilder ss = (SpannableStringBuilder)Html.fromHtml(text);
        ss.setSpan(new MyLeadingMarginSpan2(lines, width), 0, ss.length(), 0);
        messageView.setText(ss);

        // Align the text with the image by removing the rule that the text is to the right of the image
        RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)messageView.getLayoutParams();
        int[]rules = params.getRules();
        rules[RelativeLayout.RIGHT_OF] = 0;
   }


}
6
ответ дан Ronen Yacobi 25 August 2018 в 14:58
поделиться
  • 1
    Привет, Ронен. Im имеет огромные проблемы с пониманием всей идеи проблемы «обтекания изображения вокруг изображения». Не могли бы вы рассказать мне, где я могу получить некоторую информацию о том, как писать такой код? Я хотел бы узнать, как писать код самостоятельно, а не просто копировать его. – Ramona 9 August 2017 в 14:31
  • 2
    Привет @Ramona Возможно, посмотрите на эту библиотеку: github.com/deano2390/FlowTextView – Ronen Yacobi 29 August 2017 в 08:31
  • 3
    Привет, спасибо за информацию. Вы знаете, как достичь этого с изображением, размещенным в правой части экрана? – Ramona 14 October 2017 в 09:18
Другие вопросы по тегам:

Похожие вопросы: