Как можно достичь Gmail, как масштабирование и прокрутка заголовка - PullRequest
0 голосов
/ 07 февраля 2019

Как добиться такого масштабирования, как в приложении Gmail?Я поместил контейнер заголовка в ScrollView, за которым следует WebView.Кажется, это очень сложное поведение.

Здесь без увеличения.

enter image description here

Когда мы сжимаемВерхний контейнер веб-просмотра прокручивается в соответствии с увеличением:

enter image description here

Пока вот мои инициалы:

  <?xml version="1.0" encoding="utf-8"?>
  <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:background="@color/white">

    <RelativeLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:background="@color/white"
       android:fitsSystemWindows="true">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@color/white">

        </android.support.v7.widget.Toolbar>
    </android.support.design.widget.AppBarLayout>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/appbar">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <FrameLayout
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:background="@color/colorPrimary"></FrameLayout>

            <WebView
                android:id="@+id/webView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/white"
                android:scrollbars="none" />
        </LinearLayout>
    </ScrollView>
  </RelativeLayout>
</FrameLayout>

Ответы [ 3 ]

0 голосов
/ 03 марта 2019

Мне кажется, я понял ваш вопрос.Вы хотите подтолкнуть строку темы в направлении вверх, а другие сообщения электронной почты - в направлении вниз при расширении сообщения.Я попытался реализовать идею показа электронной почты в приложении Gmail.Я думаю, что я очень близок к решению, так как нажатие недостаточно плавное.Тем не менее, я хотел бы поделиться ответом здесь, чтобы представить свою мысль о вашем вопросе.

Я создал репозиторий GitHub , где вы можете увидеть мою реализацию.Я добавил туда readme, чтобы объяснить общую идею.

Я пытался реализовать все это, используя RecyclerView с разными ViewType с.Я добавил адаптер, который выглядит следующим образом.

public class RecyclerViewWithHeaderFooterAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private static final int HEADER_VIEW = 1;
    private static final int GROUPED_VIEW = 2;
    private static final int EXPANDED_VIEW = 3;

    private ArrayList<Integer> positionTracker; // Take any list that matches your requirement.
    private Context context;
    private ZoomListener zoomListener;

    // Define a constructor
    public RecyclerViewWithHeaderFooterAdapter(Context context, ZoomListener zoomListener) {
        this.context = context;
        this.zoomListener = zoomListener;
        positionTracker = Utilities.populatePositionsWithDummyData();
    }

    // Define a ViewHolder for Header view
    public class HeaderViewHolder extends ViewHolder {
        public HeaderViewHolder(View itemView) {
            super(itemView);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Do whatever you want on clicking the item
                }
            });
        }
    }

    // Define a ViewHolder for Expanded view
    public class ExpandedViewHolder extends ViewHolder {
        public ExpandedViewHolder(View itemView) {
            super(itemView);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Do whatever you want on clicking the item
                }
            });
        }
    }

    // Define a ViewHolder for Expanded view
    public class GroupedViewHolder extends ViewHolder {
        public GroupedViewHolder(View itemView) {
            super(itemView);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Do whatever you want on clicking the item
                }
            });
        }
    }

    // And now in onCreateViewHolder you have to pass the correct view
    // while populating the list item.
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View v;

        if (viewType == EXPANDED_VIEW) {
            v = LayoutInflater.from(context).inflate(R.layout.list_item_expanded, parent, false);
            ExpandedViewHolder vh = new ExpandedViewHolder(v);
            return vh;
        } else if (viewType == HEADER_VIEW) {
            v = LayoutInflater.from(context).inflate(R.layout.list_item_header, parent, false);
            HeaderViewHolder vh = new HeaderViewHolder(v);
            return vh;
        } else {
            v = LayoutInflater.from(context).inflate(R.layout.list_item_grouped, parent, false);
            GroupedViewHolder vh = new GroupedViewHolder(v);
            return vh;
        }
    }

    // Now bind the ViewHolder in onBindViewHolder
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

        try {
            if (holder instanceof ExpandedViewHolder) {
                ExpandedViewHolder vh = (ExpandedViewHolder) holder;
                vh.bindExpandedView(position);
            } else if (holder instanceof GroupedViewHolder) {
                GroupedViewHolder vh = (GroupedViewHolder) holder;
            } else if (holder instanceof HeaderViewHolder) {
                HeaderViewHolder vh = (HeaderViewHolder) holder;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // Now the critical part. You have return the exact item count of your list
    // I've only one footer. So I returned data.size() + 1
    // If you've multiple headers and footers, you've to return total count
    // like, headers.size() + data.size() + footers.size()

    @Override
    public int getItemCount() {
        return DEMO_LIST_SIZE; // Let us consider we have 6 elements. This can be replaced with email chain size
    }

    // Now define getItemViewType of your own.
    @Override
    public int getItemViewType(int position) {
        if (positionTracker.get(position).equals(HEADER_VIEW)) {
            // This is where we'll add the header.
            return HEADER_VIEW;
        } else if (positionTracker.get(position).equals(GROUPED_VIEW)) {
            // This is where we'll add the header.
            return GROUPED_VIEW;
        } else if (positionTracker.get(position).equals(EXPANDED_VIEW)) {
            // This is where we'll add the header.
            return EXPANDED_VIEW;
        }

        return super.getItemViewType(position);
    }

    // So you're done with adding a footer and its action on onClick.
    // Now set the default ViewHolder for NormalViewHolder
    public class ViewHolder extends RecyclerView.ViewHolder {
        // Define elements of a row here
        public ViewHolder(View itemView) {
            super(itemView);
            // Find view by ID and initialize here
        }

        public void bindExpandedView(final int position) {
            // bindExpandedView() method to implement actions
            final WebView webView = itemView.findViewById(R.id.email_details_web_view);
            webView.getSettings().setBuiltInZoomControls(true);
            webView.getSettings().setDisplayZoomControls(false);
            webView.loadUrl("file:///android_asset/sample.html");
            webView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
                @Override
                public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
                    zoomListener.onZoomListener(position);
                }
            });
        }
    }
}

И элемент расширенного списка содержит WebView, у которого есть оболочка wrap_content.Вы найдете следующий макет в list_item_expanded.xml.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <WebView
        android:id="@+id/email_details_web_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/white"
        android:scrollbars="none"
        tools:ignore="WebViewLayout" />
</RelativeLayout>

Я попытался добавить несколько фиктивных данных для эксперимента, и поэтому был написан класс UtilityRecyclerView задано обратное расположение, так как это обычное ожидание показа разговора в RecyclerView.

Ключевой идеей является scrollToPosition, когда WebView расширяется.Так что создается впечатление, что предметы толкаются вверх и вниз, чтобы приспособиться к расширению.Надеюсь, вы поняли идею.

Я добавляю несколько скриншотов, чтобы дать вам представление о том, чего я мог достичь до сих пор.

enter image description here

Обратите внимание, что толкающий механизм не плавный.Я буду работать над этим.Тем не менее, я подумал, что должен опубликовать это здесь, так как это может помочь вам в вашем мышлении.Я хотел бы предложить вам клонировать репозиторий и запустить приложение, чтобы проверить общую реализацию.Дайте мне знать, если есть какие-либо отзывы.

0 голосов
/ 04 марта 2019

Я попытался реализовать это поведение, используя webview.setBuiltInZoomControls() и ScaleGestureDetector, переопределив WebView и передав все события движения в детектор.Это работает, но детектор масштаба и масштабирование работают немного по-разному, и UX оказывается ужасным.

Если вы внимательно посмотрите на реализацию масштабирования Gmail и масштабирование в веб-просмотре, вы увидите, что они разные.Я считаю, что зум Gmail основан на view.setScaleX() и view.setScaleY().Вы можете получить базовое поведение, создав подкласс WebView и следуя этому руководству .Вам также может понадобиться позвонить view.setPivotX() и view.setPivotY().Реализация Gmail более сложна, поскольку имеет прокрутку и, кажется, прокручивает содержимое вверх при увеличении. Вы можете попробовать использовать некоторую библиотеку, которая реализует масштабируемый контейнер и поддерживает прокрутку, например , этот .Однако мне не удалось заставить его работать должным образом с WebView.

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

0 голосов
/ 26 февраля 2019

GMail использует Chrome WebView с включенным масштабированием.зум применяется только к виду с одной нитью.WebSettings setBuiltInZoomControls () по умолчанию false и setDisplayZoomControls () по умолчанию true.изменяя оба, масштабирование работает, и элементы управления масштабированием не отображаются:

webview.getSettings().setBuiltInZoomControls(true);
webview.getSettings().setDisplayZoomControls(false);

, и эта панель инструментов имеет прозрачный стиль ActionBar со стилем windowActionBarOverlay set true:

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


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

webView.setOnScrollChangeListener(new View.OnScrollChangeListener() {
    @Override
    public void onScrollChange(View view, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
        if(scrollY == 0) {
            /* remove the ActionBar's bottom shadow  */
        } else {
            /* apply the ActionBar's bottom shadow */
        }
    }
}

в зависимости от того, как часто срабатывает OnScrollChangeListener, даже проверки на scrollY == 0 и scrollY == 1 может быть достаточно длявключите и выключите тень.


при масштабировании это выглядит как ScaleGestureDetector.SimpleOnScaleGestureListener (см. документы ), где .getScaleFactor() используется для анимации вторичного объекта.«Панель инструментов» вертикальная верхняя позиция, которая затем толкает ее за пределы видимого окна просмотра.и эта вторичная «панель инструментов» выглядит как вложенная вертикаль DrawerLayout - которую нельзя перемещать вручную - вот почему она перемещается так плавно ... a DrawerLayout не ограничивается горизонтальным выдвижным ящиком;и я думаю, что это ответ.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...