Android-skin-support icon indicating copy to clipboard operation
Android-skin-support copied to clipboard

通过AsyncLayoutInflater inflate的View有什么办法换肤?

Open GuoJinyu opened this issue 5 years ago • 2 comments
trafficstars

GuoJinyu avatar Apr 23 '20 11:04 GuoJinyu

你好,请问你是怎么解决的,修改布局文件吗?有没有更好的方法

a741762308 avatar Dec 29 '21 02:12 a741762308

基于系统AsyncLayoutInflater实现SkinAsyncLayoutInflater,传入一个支持换肤的LayoutInflater即可。

示例:

LayoutInflater layoutInflater = LayoutInflater.from(this);
ViewGroup rootView = (ViewGroup) layoutInflater.inflate(R.layout.layout_placeholder, null);
setContentView(rootView);
new SkinAsyncLayoutInflater(layoutInflater, this).inflate(getLayoutId(), null, (view, resid, parent) -> {
    rootView.addView(view);
    //todo 初始化数据
});

SkinAsyncLayoutInflater.java

import android.content.Context;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.core.util.Pools;

import java.util.concurrent.ArrayBlockingQueue;


/**
 * SkinAsyncLayoutInflater 使用外部LayoutInflater
 * @see androidx.asynclayoutinflater.view.AsyncLayoutInflater
 */
public final class SkinAsyncLayoutInflater {
    private static final String TAG = SkinAsyncLayoutInflater.class.getSimpleName();

    LayoutInflater mInflater;
    Handler mHandler;
    InflateThread mInflateThread;

    public SkinAsyncLayoutInflater(LayoutInflater layoutInflater, Context context) {
        mInflater = layoutInflater;
        mHandler = new Handler(mHandlerCallback);
        mInflateThread = InflateThread.getInstance();
    }

    @UiThread
    public void inflate(@LayoutRes int resid, @Nullable ViewGroup parent,
                        @NonNull OnInflateFinishedListener callback) {
        if (callback == null) {
            throw new NullPointerException("callback argument may not be null!");
        }
        InflateRequest request = mInflateThread.obtainRequest();
        request.inflater = this;
        request.resid = resid;
        request.parent = parent;
        request.callback = callback;
        mInflateThread.enqueue(request);
    }

    private Callback mHandlerCallback = new Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            InflateRequest request = (InflateRequest) msg.obj;
            if (request.view == null) {
                request.view = mInflater.inflate(request.resid, request.parent, false);
            }
            request.callback.onInflateFinished(request.view, request.resid, request.parent);
            mInflateThread.releaseRequest(request);
            return true;
        }
    };

    public interface OnInflateFinishedListener {
        void onInflateFinished(@NonNull View view, @LayoutRes int resid, @Nullable ViewGroup parent);
    }

    private static class InflateRequest {
        SkinAsyncLayoutInflater inflater;
        ViewGroup parent;
        int resid;
        View view;
        OnInflateFinishedListener callback;

        InflateRequest() {
        }
    }

    private static class InflateThread extends Thread {
        private static final InflateThread sInstance;

        static {
            sInstance = new InflateThread();
            sInstance.start();
        }

        public static InflateThread getInstance() {
            return sInstance;
        }


        private ArrayBlockingQueue<InflateRequest> mQueue = new ArrayBlockingQueue<>(10);
        private Pools.SynchronizedPool<InflateRequest> mRequestPool = new Pools.SynchronizedPool<>(10);

        // Extracted to its own method to ensure locals have a constrained liveness
        // scope by the GC. This is needed to avoid keeping previous request references
        // alive for an indeterminate amount of time, see b/33158143 for details
        public void runInner() {
            InflateRequest request;
            try {
                request = mQueue.take();
            } catch (InterruptedException ex) {
                // Odd, just continue
                Log.w(TAG, ex);
                return;
            }

            try {
                request.view = request.inflater.mInflater.inflate(request.resid, request.parent, false);
            } catch (RuntimeException ex) {
                // Probably a Looper failure, retry on the UI thread
                Log.w(TAG, "Failed to inflate resource in the background! Retrying on the UI thread", ex);
            }
            Message.obtain(request.inflater.mHandler, 0, request).sendToTarget();
        }

        @Override
        public void run() {
            while (true) {
                runInner();
            }
        }

        public InflateRequest obtainRequest() {
            InflateRequest obj = mRequestPool.acquire();
            if (obj == null) {
                obj = new InflateRequest();
            }
            return obj;
        }

        public void releaseRequest(InflateRequest obj) {
            obj.callback = null;
            obj.inflater = null;
            obj.parent = null;
            obj.resid = 0;
            obj.view = null;
            mRequestPool.release(obj);
        }

        public void enqueue(InflateRequest request) {
            try {
                mQueue.put(request);
            } catch (InterruptedException e) {
                throw new RuntimeException("Failed to enqueue async inflate request", e);
            }
        }
    }
}

你好,请问你是怎么解决的,修改布局文件吗?有没有更好的方法

ya0xu avatar Nov 24 '23 11:11 ya0xu