Chuyển link url trong text thành button

1. Ý tưởng

Trong 1 đoạn chat nhiều lúc chúng ta thường hay đính kèm theo url để gửi cho bạn bè nhưng chúng ta lại không muốn nó hiển thị trực tiếp vì thường các link đó quá dài -> Do đó mình có ý tưởng chuyển các url đó thành các button cho thân thiện.

2. Base Adapter

Mình đã hướng dẫn ở 1 bài viblo trước đây BaseAdapter Trong bài viết này mình sẽ dùng BaseAdapter trên link ở trên hoặc bạn có thể xem trực tiếp ở đây BaseAdapter

3. Các layout item của recycler view

3.1. item_recycler_view_button.xml

Đây là button sẽ hiển thị thay url trong text.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <Button
        android:id="@+id/btn_open"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Open"
        android:textSize="14sp"/>
</layout>

3.2. item_recycler_view_text.xml

Đây là item text view hiển thị text sau khi đã loại bỏ url

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="viewModel"
            type="com.vutuananh.changeurltobutton.model.ViewModel"/>
    </data>

    <TextView
        android:id="@+id/tv_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@{viewModel.text}"
        android:textColor="@android:color/black"
        android:textSize="14sp"/>
</layout>

3.3. item_recycler_view_message.xml

Đây là item chính nó gồm 2 thành phần chính là

  • 1 text view sẽ hiển thị khi text truyền vào ko có chứa url
  • 1 recycler view sẽ hiển thị khi text truyền vào có chứa url -> Recycler view này sẽ có 2 loại item là text view và button (thay thế url)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="viewModel"
            type="String"/>
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:background="@drawable/bg_text_view"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_message"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{viewModel}"
            android:textColor="@android:color/black"
            android:textSize="14sp"/>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_url"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:visibility="gone"/>
    </LinearLayout>
</layout>

4. activity_main.xml

Thành phần chính là 1 reycler view

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.vutuananh.changeurltobutton.MainActivity">

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

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_message"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </LinearLayout>
</layout>

5. ViewModel

public static final int TYPE_TEXT = 1;
    public static final int TYPE_URL = 2;
    private String text;
    private int type;

    public ViewModel(String text) {
        this.text = text;
        type = TYPE_TEXT;
    }

    public ViewModel(String text, int type) {
        this.text = text;
        this.type = type;
    }

    public String getText() {
        return text;
    }

    public int getType() {
        return type;
    }

6. UrlPosition

 public static class UrlPosition {
        private String url;
        private int start;
        private int end;

        public UrlPosition(String url, int start, int end) {
            this.url = url;
            this.start = start;
            this.end = end;
        }

        public String getUrl() {
            return url;
        }

        public int getStart() {
            return start;
        }

        public int getEnd() {
            return end;
        }
    }

7. MainActivity

7.1. Khởi tạo data test

private static final String[] ARRAY = {
        "Bản dịch từ: https://ksylvest.com/posts/2017-08-12/fabrication-vs-factorygirl",
        "http://s2phim.net/xem-phim/nguoi-hau-cua-thieu-gia-mu/141347",
        "Đại gia Hà Nội đổi 10 cây vàng lấy sanh cổ gây “chấn động” một thời",
        "Giá vàng hôm nay 2/5: Bất ngờ giảm mạnh sau kì nghỉ lễ",
        "Trong tuần trước, vàng SJC mặc dù đã đứng sát ngưỡng 37 triệu đồng nhưng không thể chinh phục mốc này. Trong những phiên giữa tuần, vàng SJC hụt hơi và đã có lúc chỉ còn 36,75 triệu đồng. Phải tới phiên cuối cùng trước kỳ nghỉ lễ, giá vàng mới bật tăng trở lại mốc 36,84 triệu đồng mỗi lượng.",
        "Converting array to list in Java\n https://stackoverflow.com/questions/2607289/converting-array-to-list-in-java",
        "Fabrication hay FactoryGirl nhanh hơn khi viết Rspec https://viblo.asia https://viblo.asia https://viblo.asia https://viblo.asia Viblo"
    };

7.2. getUrlList() Function tách string truyền vào thành 1 list data gồm 2 loại là text và url

public List<ViewModel> getUrlList(String text) {
        if (TextUtils.isEmpty(text) || TextUtils.isEmpty(text.trim())) {
            return null;
        }
        Matcher urlMatcher = Patterns.WEB_URL.matcher(text);
        List<ViewModel.UrlPosition> urlPositionList = new ArrayList<>();
        while (urlMatcher.find()) {
            int start = urlMatcher.start();
            int end = urlMatcher.end();
            String url = text.substring(start, end);
            urlPositionList.add(new ViewModel.UrlPosition(url, start, end));
        }
        List<ViewModel> viewModelList = new ArrayList<>();
        if (urlPositionList.isEmpty()) return null;
        int start = 0;
        for (ViewModel.UrlPosition urlPosition : urlPositionList) {
            String startStr = text.substring(start, urlPosition.getStart());
            if (!TextUtils.isEmpty(startStr.trim())) {
                viewModelList.add(new ViewModel(startStr));
            }
            viewModelList.add(new ViewModel(urlPosition.getUrl(), ViewModel.TYPE_URL));
            start = urlPosition.getEnd();
            if (urlPositionList.indexOf(urlPosition) == urlPositionList.size() - 1) {
                if (start < text.length()) {
                    String endStr = text.substring(start, text.length());
                    if (!TextUtils.isEmpty(endStr.trim())) {
                        viewModelList.add(new ViewModel(endStr));
                    }
                }
            }
        }
        return viewModelList;
    }

7.3. Khởi tạo recycler view chính

  • Chúng ta sẽ sử dụng SingleTypeAdapter để làm adapter chính
  • Arrays.asList(ARRAY) là để chuyển array String ở trên thành List
        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        final SingleTypeAdapter<String> adapter = new SingleTypeAdapter<>(this, R.layout.item_recycler_view_message);
        adapter.addAll(Arrays.asList(ARRAY));
        mBinding.recyclerMessage.setAdapter(adapter);
        mBinding.recyclerMessage.setLayoutManager(new LinearLayoutManager(this));

7.4. Reycler view lồng recycler view

Chú ý dưới đây chúng ta sẽ xử lý recycler view lồng trong recycler view

Đoạn dưới chúng ta check xem có url trong string truyền vào adapter cha không : Nếu không có thì ẩn recycler view và hiển thị text view như bình thường, nếu có thì hiển thị recycler view và ẩn text view

   final List<ViewModel> viewModelList = getUrlList(adapter.get(position));
                final ItemRecyclerViewMessageBinding binding = (ItemRecyclerViewMessageBinding) holder.getBinding();
                if (viewModelList == null || viewModelList.isEmpty()) {
                    binding.tvMessage.setVisibility(View.VISIBLE);
                    binding.recyclerUrl.setVisibility(View.GONE);
                    return;
                }
                binding.tvMessage.setVisibility(View.GONE);
                binding.recyclerUrl.setVisibility(View.VISIBLE);
  • Vì chúng ta có 2 loại item là button và text nên chúng ta sẽ sử dụng MultiTypeAdapter để làm adapter chính
adapter.setDecorator(new BaseViewAdapter.Decorator() {
            @Override
            public void decorator(BindingViewHolder holder, int position, int viewType) {
                final List<ViewModel> viewModelList = getUrlList(adapter.get(position));
                final ItemRecyclerViewMessageBinding binding = (ItemRecyclerViewMessageBinding) holder.getBinding();
                if (viewModelList == null || viewModelList.isEmpty()) {
                    binding.tvMessage.setVisibility(View.VISIBLE);
                    binding.recyclerUrl.setVisibility(View.GONE);
                    return;
                }
                binding.tvMessage.setVisibility(View.GONE);
                binding.recyclerUrl.setVisibility(View.VISIBLE);
                MultiTypeAdapter adapterUrl = new MultiTypeAdapter(MainActivity.this);
                adapterUrl.addViewTypeToLayoutMap(ViewModel.TYPE_TEXT, R.layout.item_recycler_view_text);
                adapterUrl.addViewTypeToLayoutMap(ViewModel.TYPE_URL, R.layout.item_recycler_view_button);
                adapterUrl.addAll(viewModelList, new MultiTypeAdapter.MultiViewTyper() {
                    @Override
                    public int getViewType(Object item) {
                        if (item instanceof ViewModel) {
                            return ((ViewModel) item).getType();
                        }
                        return 0;
                    }
                });
                adapterUrl.setDecorator(new BaseViewAdapter.Decorator() {
                    @Override
                    public void decorator(BindingViewHolder holder, int positionChild, int viewType) {
                        if (holder.getBinding() instanceof ItemRecyclerViewButtonBinding) {
                            ItemRecyclerViewButtonBinding buttonBinding = (ItemRecyclerViewButtonBinding) holder.getBinding();
                            final String url = viewModelList.get(positionChild).getText();
                            buttonBinding.btnOpen.setOnClickListener(new View.OnClickListener() {
                                @Override
                                public void onClick(View v) {
                                    openLinkInBrowser(MainActivity.this, url);
                                }
                            });
                        }
                    }
                });
                binding.recyclerUrl.setAdapter(adapterUrl);
                binding.recyclerUrl.setLayoutManager(new LinearLayoutManager(MainActivity.this));
            }
        });

Hình ảnh

Code

ChangeUrlToButton