package com.example.qyh.joe.theme.base;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.util.ArrayMap;
import android.support.v4.view.LayoutInflaterCompat;
import android.support.v4.view.LayoutInflaterFactory;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.AppCompatDelegate;
import android.util.AttributeSet;
import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.View;
import com.example.qyh.joe.theme.SkinManager;
import com.example.qyh.joe.theme.attr.SkinAttr;
import com.example.qyh.joe.theme.attr.SkinAttrSupport;
import com.example.qyh.joe.theme.attr.SkinView;
import com.example.qyh.joe.theme.callback.ISkinChangedListener;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Created by zhy on 15/9/22.
*/
public class BaseSkinActivity extends AppCompatActivity implements ISkinChangedListener, LayoutInflaterFactory
{
static final Class<?>[] sConstructorSignature = new Class[]{
Context.class, AttributeSet.class};
private static final Map<String, Constructor<? extends View>> sConstructorMap
= new ArrayMap<>();
private final Object[] mConstructorArgs = new Object[2];
private static Method sCreateViewMethod;
static final Class<?>[] sCreateViewSignature = new Class[]{View.class, String.class, Context.class, AttributeSet.class};
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs)
{
LayoutInflater layoutInflater = getLayoutInflater();
AppCompatDelegate delegate = getDelegate();
View view = null;
try
{
if (sCreateViewMethod == null)
{
Method methodOnCreateView = delegate.getClass().getMethod("createView", sCreateViewSignature);
sCreateViewMethod = methodOnCreateView;
}
Object object = sCreateViewMethod.invoke(delegate, parent, name, context, attrs);
view = (View) object;
} catch (NoSuchMethodException e)
{
e.printStackTrace();
} catch (InvocationTargetException e)
{
e.printStackTrace();
} catch (IllegalAccessException e)
{
e.printStackTrace();
}
List<SkinAttr> skinAttrList = SkinAttrSupport.getSkinAttrs(attrs, context);
if (skinAttrList.isEmpty())
{
return view;
}
if (view == null)
{
view = createViewFromTag(context, name, attrs);
}
injectSkin(view, skinAttrList);
return view;
}
private void injectSkin(View view, List<SkinAttr> skinAttrList)
{
//do some skin inject
if (skinAttrList.size() != 0)
{
List<SkinView> skinViews = SkinManager.getInstance().getSkinViews(this);
if (skinViews == null)
{
skinViews = new ArrayList<SkinView>();
}
SkinManager.getInstance().addSkinView(this, skinViews);
skinViews.add(new SkinView(view, skinAttrList));
if (SkinManager.getInstance().needChangeSkin())
{
SkinManager.getInstance().apply(this);
}
}
}
private View createViewFromTag(Context context, String name, AttributeSet attrs)
{
if (name.equals("view"))
{
name = attrs.getAttributeValue(null, "class");
}
try
{
mConstructorArgs[0] = context;
mConstructorArgs[1] = attrs;
if (-1 == name.indexOf('.'))
{
// try the android.widget prefix first...
return createView(context, name, "android.widget.");
} else
{
return createView(context, name, null);
}
} catch (Exception e)
{
// We do not want to catch these, lets return null and let the actual LayoutInflater
// try
return null;
} finally
{
// Don't retain references on context.
mConstructorArgs[0] = null;
mConstructorArgs[1] = null;
}
}
private View createView(Context context, String name, String prefix)
throws ClassNotFoundException, InflateException
{
Constructor<? extends View> constructor = sConstructorMap.get(name);
try
{
if (constructor == null)
{
// Class not found in the cache, see if it's real, and try to add it
Class<? extends View> clazz = context.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
constructor = clazz.getConstructor(sConstructorSignature);
sConstructorMap.put(name, constructor);
}
constructor.setAccessible(true);
return constructor.newInstance(mConstructorArgs);
} catch (Exception e)
{
// We do not want to catch these, lets return null and let the actual LayoutInflater
// try
return null;
}
}
protected void onCreate(@Nullable Bundle savedInstanceState)
{
LayoutInflater layoutInflater = LayoutInflater.from(this);
LayoutInflaterCompat.setFactory(layoutInflater, this);
super.onCreate(savedInstanceState);
SkinManager.getInstance().addChangedListener(this);
}
@Override
protected void onDestroy()
{
super.onDestroy();
SkinManager.getInstance().removeChangedListener(this);
}
@Override
public void onSkinChanged()
{
SkinManager.getInstance().apply(this);
}
}