I have 2 View classes that implement the same functionality (display images). One is used for backwards compatibility and extends ImageView, while the other extends TextureView but is available only in API 14+. I set a custom factory to pick the right class to use at runtime (you place MyViewProxy in your XML layouts).
class View1 extends ImageView
{
...
}
class View2 extends TextureView // TextureView extends View (not ImageView)
{
...
}
class MyViewProxy extends ImageView
{
...
}
private static class MyViewFactory implements LayoutInflater.Factory
{
@Override
public View onCreateView( String name, Context context, AttributeSet attrs )
{
if( 0 == name.compareTo( "MyViewProxy" ) )
{
if( android.os.Build.VERSION.SDK_INT < 14 )
{
return new View1( context, attrs );
}
else
{
return new View2( context, attrs );
}
}
return null;
}
}
This all works fine, except for the part where View2 (which does not extend ImageView) should detect the default "src" image to display, like ImageView does. View1 extends ImageView, so it already works fine.
The goal: to be able to convert the "src" attribute from XML into a resource id, and open the resource using getResources().openRawResource( resId ). The value of the src attribute is typically in the form "@drawable/image".
For reference, here's how ImageView sets its default image:
public ImageView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
initImageView();
TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.ImageView, defStyle, 0);
Drawable d = a.getDrawable(com.android.internal.R.styleable.ImageView_src);
if (d != null)
{
setImageDrawable(d);
}
...
}
I tried replicating this code in View2's constructor, but got "undefined reference" for com.android.internal.R. I thought I might use reflection to circumvent this, although that's probably a sketchy thing to do.
Here's what I've got so far. This code does not work yet
public View2( Context context, AttributeSet attrs, int defStyle )
{
...
Class<?> clazz = Class.forName( "com.android.internal.R$styleable" );
Field ImageView_field = clazz.getField( "ImageView" );
Field ImageView_src_field = clazz.getField( "ImageView_src" );
if( ImageView_field != null && ImageView_src_field != null )
{
if( ImageView_field.getType().isArray() )
{
Class<?> type = ImageView_field.getType().getComponentType();
Object array = ImageView_field.get( clazz );
if( type == Integer.TYPE )
{
int[] ImageView_intArray = (int[]) array;
TypedArray aInternal = context.obtainStyledAttributes( attrs, ImageView_intArray, defStyle, 0 );
int srcId = ImageView_src_field.getInt( clazz );
String src = aInternal.getString( srcId );
if( src != null )
{
File testfile = new File( src );
boolean exists = testfile.exists();
//InputStream is = context.getAssets().open( src ); // throws FileNotFoundException
int resId = context.getResources().getIdentifier( src, "drawable", context.getPackageName() ); // returns 0
InputStream is = context.getResources().openRawResource( resId );
if( is != null )
{
}
}
aInternal.recycle();
}
}
}
}
Using reflection, I can access the "src" variable correctly, which in my test is "res/drawable-hdpi/image.bmp" (from "@drawable/image" in XML). From there, how do I actually open it? AssetManager.open() throws a FileNotFoundException, Resources.getIdentifier() returns 0, and File.exists() returns false.
Am I even approaching this problem correctly? Will reflection jeopardize the future-proof-ness of my code?