Android Context与接口的强制类型转换问题

时间:2022-06-08 17:55:18

   昨天在开发过程中遇到一个问题,困扰了很长时间,并且直到现在也是没怎么解决.和老师讨论了一上午也没有个结果,先在此记录一下.

   首先说明的是我要实现的效果如下

Android Context与接口的强制类型转换问题

类似QQ的这个长按出现待办和删除的按钮,具体的实现大家可以去这里:http://www.eoeandroid.com/thread-326918-1-1.html.

需求就是这样,他给出的例子是在Activity中实现的,这样贴一下具体代码:

首先是这个待办和删除的自定义Dialog:

package com.example.listitemdeletedemo;

import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;

/**
* @文件名称: MyDialog.java
* @功能描述: 自定义dialog
* @版本信息: Copyright (c)2014
* @开发人员: vincent
* @版本日志: 1.0
* @创建时间: 2014年3月18日 下午1:45:38
*/
public class MyDialog extends Dialog implements OnClickListener {
private TextView leftTextView, rightTextView;
private IDialogOnclickInterface dialogOnclickInterface;
private Context context;

public MyDialog(Context context, int theme) {
super(context, theme);
this.context = context;
// TODO Auto-generated constructor stub
}

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_dialog);

leftTextView = (TextView) findViewById(R.id.textview_one);
rightTextView = (TextView) findViewById(R.id.textview_two);
leftTextView.setOnClickListener(this);
rightTextView.setOnClickListener(this);
}

@Override
public void onClick(View v) {
<span style="color:#ff6600;">// 注意这一句</span>
<span style="color:#33cc00;">dialogOnclickInterface = (IDialogOnclickInterface) context;</span>
switch (v.getId()) {
case R.id.textview_one:
dialogOnclickInterface.leftOnclick();
break;
case R.id.textview_two:
dialogOnclickInterface.rightOnclick();
break;
default:
break;
}
}

<span style="color:#33ff33;"> public interface IDialogOnclickInterface {
void leftOnclick();

void rightOnclick();
}</span>
}

需要注意的是,我用红色和绿色高亮显示的部分,我们下面的问题全部围绕标记的代码展开!

最开始的时候我对于这句很怀疑

<span style="color:#33cc00;">dialogOnclickInterface = (IDialogOnclickInterface) context;</span>
对你没看错,把上下文的类型强制转换成了一个接口类型,这一点我很疑惑,为什么会这么转型?以前的开发过程中从未看过,也从未这么转过,但是最重要的是他能运行,下面是他Activity的写法:
<pre name="code" class="java">public class MainActivity extends Activity implements IDialogOnclickInterface {private ListView listView;private ListViewAdapter listViewAdapter;private List<String> dataList;private int longClickPosition;private MyDialog myDialog;private static final String TAG = "MainActivity";private View currentItemView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);listView = (ListView) findViewById(R.id.listview);myDialog = new MyDialog(this, R.style.MyDialogStyle);dataList = new ArrayList<String>();for (int i = 0; i < 19; i++) {dataList.add("data " + i);}listViewAdapter = new ListViewAdapter(this, dataList);listView.setAdapter(listViewAdapter);listView.setOnItemLongClickListener(new OnItemLongClickListener() {@Overridepublic boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {// TODO Auto-generated method stubint[] location = new int[2];// 获取当前view在屏幕中的绝对位置// ,location[0]表示view的x坐标值,location[1]表示view的坐标值view.getLocationOnScreen(location);view.setBackgroundColor(getResources().getColor(R.color.blue));currentItemView = view;longClickPosition = position;DisplayMetrics displayMetrics = new DisplayMetrics();Display display = MainActivity.this.getWindowManager().getDefaultDisplay();display.getMetrics(displayMetrics);WindowManager.LayoutParams params = myDialog.getWindow().getAttributes();params.gravity = Gravity.BOTTOM;params.y =display.getHeight() -  location[1];myDialog.getWindow().setAttributes(params);myDialog.setCanceledOnTouchOutside(true);myDialog.show();return false;}});myDialog.setOnCancelListener(new OnCancelListener() {@Overridepublic void onCancel(DialogInterface dialog) {// TODO Auto-generated method stubcurrentItemView.setBackgroundColor(getResources().getColor(android.R.color.white));}});}@Overridepublic void leftOnclick() {// TODO Auto-generated method stubmyDialog.dismiss();currentItemView.setBackgroundColor(getResources().getColor(android.R.color.white));dataList.remove(longClickPosition);listViewAdapter.notifyDataSetChanged();}@Overridepublic void rightOnclick() {// TODO Auto-generated method stubmyDialog.dismiss();currentItemView.setBackgroundColor(getResources().getColor(android.R.color.white));dataList.remove(longClickPosition);listViewAdapter.notifyDataSetChanged();}}<span style="font-family: Arial, Helvetica, sans-serif;">                                                                                                                              </span>
<span style="font-family: Arial, Helvetica, sans-serif;">可以看到的是他的Activity实现了这个接口,而且实现了接口所有的方法,而且我很明确的告诉你,这个是可以完美运行的,效果如下:</span>
<span style="font-family: Arial, Helvetica, sans-serif;"><img src="http://a1.eoeandroid.com/attachment/forum/201403/19/110840cqfcfobjjb35q5bq.png" alt="" /></span>
<span style="font-family: Arial, Helvetica, sans-serif;"></span>
<span style="font-family: Arial, Helvetica, sans-serif;">这个就是那个示例程序,而且是非常好的一个效果!我当时在做的使用用的是setOnCreateContextMenuListener方法来写,实现删除操作的,效果差,而且很笨的感觉!好吧,既然这个例子这么好,那我就开始移植吧,我的ListView是放在Fragment中的,理所当然的是在Fragment中来实现这个接口.因为涉及项目的内容,我只能贴部分代码在这里,下面是我的Fragment的代码:</span>
<span style="font-family: Arial, Helvetica, sans-serif;"></span>
<span style="font-family: Arial, Helvetica, sans-serif;"></span><pre name="code" class="java">public class ChatFragment extends Fragment implements IDialogOnclickInterface{private Context context;private ListView listView;private PullToRefreshListView mPullToRefreshListView;private List<ChatFragmentMsg> list = new ArrayList<ChatFragmentMsg>();private LvAdapter adapter;private boolean hasMsg = false;private int longClickPosition;public static MyDialog myDialog;private View currentItemView;@Overridepublic void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {View view = inflater.inflate(R.layout.fragment_chat, null);context = view.getContext();<span style="color:#ff0000;">// 初始化选项(问题所在)myDialog = new MyDialog(context, R.style.MyDialogStyle);</span>listView = (ListView) view.findViewById(R.id.chat_msg_list);// 创建下拉刷新的ListViewmPullToRefreshListView = new PullToRefreshListView(getActivity());// 将ListView的宽高赋予这个组件mPullToRefreshListView.setLayoutParams(listView.getLayoutParams());adapter = new LvAdapter(context, R.layout.chat_item, list);mPullToRefreshListView.setAdapter(adapter);mPullToRefreshListView.setOnItemClickListener(new OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view,int position, long id) {/** *  进入聊天页面 */Toast.makeText(context,mPullToRefreshListView.getAdapter().getItem(position).toString(), Toast.LENGTH_SHORT).show();}});mPullToRefreshListView.setOnItemLongClickListener(new OnItemLongClickListener() {@Overridepublic boolean onItemLongClick(AdapterView<?> parent, View view,int position, long id) {int[] location = new int[2];// 获取当前view在屏幕中的绝对位置// ,location[0]表示view的x坐标值,location[1]表示view的坐标值view.getLocationOnScreen(location);view.setBackgroundColor(getResources().getColor(R.color.blue));currentItemView = view;longClickPosition = position;DisplayMetrics displayMetrics = new DisplayMetrics();Display display = getActivity().getWindowManager().getDefaultDisplay();display.getMetrics(displayMetrics);// 存在问题,需要解决WindowManager.LayoutParams params = myDialog.getWindow().getAttributes();params.gravity = Gravity.BOTTOM;params.y =display.getHeight() -  location[1];myDialog.getWindow().setAttributes(params);myDialog.setCanceledOnTouchOutside(true);myDialog.show();return false;}});myDialog.setOnCancelListener(new OnCancelListener() {@Overridepublic void onCancel(DialogInterface dialog) {// TODO Auto-generated method stubcurrentItemView.setBackgroundColor(getResources().getColor(android.R.color.white));}});return mPullToRefreshListView;}@Overridepublic void leftOnclick() {// TODO Auto-generated method stubToast.makeText(getActivity(),                "待办",                Toast.LENGTH_SHORT).show();}@Overridepublic void rightOnclick() {// TODO Auto-generated method stubToast.makeText(getActivity(),                "删除",                Toast.LENGTH_SHORT).show();}}
这样我就在Fragment中实现了这个接口,实现接口中的方法,还有一点需要说明的是,在我的父Activity中有三个Fragment,他们通过左划右划来切换,这个只是其中的一个Fragment,

 
<span style="font-family: Arial, Helvetica, sans-serif;">那么这样的话我就算是集成完毕了,现在运行一下,结果是报错的,错误log如下:</span>
<span style="font-family: Arial, Helvetica, sans-serif;"><img src="http://img.blog.csdn.net/20141104144013822?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMDY2NTY5MQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" /></span>
<span style="font-family: Arial, Helvetica, sans-serif;">其错误位置就定位在</span><pre name="code" class="java"><span style="color:#ff0000;">// 初始化选项(问题所在)</span>
<span style="color:#ff0000;">myDialog = new MyDialog(context, R.style.MyDialogStyle);</span>
进一步定位却是<pre name="code" class="java"> <span style="color:#ff6600;">// 注意这一句</span><span style="color:#33cc00;">dialogOnclickInterface = (IDialogOnclickInterface) context;</span>
这个位置,于是就出现了强制类型转换上的错误.
那么这个Context到底来自哪里?无论是怎么在获取Context的时候,我使用了getActivity(),也用过getApplicationContext(),这两种方式都不行,意味着这个东西确实不好解决了!
这个确实是郁闷啊,在论坛问了半天,有一个哥们提出来了,把这个接口放到你父Activity中去实现,那好吧,这样就是按照例子中来做了,这里我就不贴代码了,事实是却是可以执行下去,而且会出现最开始提到的例子中的效果,看上去这样就很好了.其实不然,这个效果并不是必须的,而且在中午的时候仔细考虑了一下,确实是影响到了我的ListView中正常执行删除和待办功能,在父Activity中实现意味着Fragment中的ListView要变成public的,这样可能会使ListView变得不安全,而且会影响Fragment中的相关项正常运转,在权衡之后决定放弃这个效果.
如果后边有时间的话,我会重新考虑实现这个效果!